引言: 在Spring Web项目中一般都会使用OpenEntityManagerInViewFilter来保证JPA session的正常关闭,在笔者的项目中,使用了Spring + Spring Data + JPA + Hibernate来的架构来组织项目,碰到了org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'entityManagerFactory' is defined 的异常信息,将过一番查找之后,方才发现问题是加载顺序的问题....
1. 项目背景介绍
项目中使用的技术有: Spring+Spring Data, JPA, Hibernate等来贯穿项目的主题架构。
2. Session异常关闭的处理机制
在Java Web项目中使用Hibernate经常会遇到LazyInitializationException 。这是因为controller和model层(java代码)将通过JPA的一些启用了延迟加载功能 的领域(如用getRefrence() 方法或者在关联关系中采用fetch=FetchType.LAZY )返回给view层(jsp代码)的时候,由于加载领域对象的JPA Session已经关闭,导致这些延迟加载的数据访问异常。
这时就可以使用OpenEntityManagerInViewFilter来将一个JPAsession与一次完整的请求过程对应的线程相绑定。
解决办法:
<filter> <filter-name>Spring OpenEntityManagerInViewFilter</filter-name> <filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class> <init-param> <!-- 指定org.springframework.orm.jpa.LocalEntityManagerFactoryBean在spring配置文件中的名称,默认值为entityManagerFactory 如果LocalEntityManagerFactoryBean在spring中的名称不是entityManagerFactory,该参数一定要指定,否则会出现找不到entityManagerFactory的例外 --> <param-name>entityManagerFactoryBeanName</param-name> <param-value>entityManagerFactory</param-value> </init-param> </filter> <filter-mapping> <filter-name>Spring OpenEntityManagerInViewFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>3. 异常问题的出现
在解决了Session异常关闭之后,在启动服务器的时候,就出现了如下异常信息:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'entityManagerFactory' is defined at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:575) at org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1111) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:276) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:195) at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1121) at org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter.lookupEntityManagerFactory(OpenEntityManagerInViewFilter.java:222) at org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter.lookupEntityManagerFactory(OpenEntityManagerInViewFilter.java:205) at org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter.doFilterInternal(OpenEntityManagerInViewFilter.java:150) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:106) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:106) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122) at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:501) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:170) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:98) at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:950) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408) at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1040) at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:607) at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:313) at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908) at java.lang.Thread.run(Thread.java:662)从异常信息的分析可知道,OpenEntityManagerInViewFilter中对entityManagerFactory有依赖,需要对其在Spring中声明的实例依赖。 但是没有找到合适的实例。
可是entityManagerFactory的确已经声明在了spring的配置文件之中了。问题在哪里呢?
4. 问题分析以及定位
项目中entityManagerFactory在Spring中的配置文件定义的,经过检查,工作正常,不存在问题。
<beans:bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <beans:property name="dataSource" ref="dataSource" /> <!-- <beans:property name="persistenceProviderClass" value="org.hibernate.ejb.HibernatePersistence"/> --> <beans:property name="jpaVendorAdapter"> <beans:bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"> <!-- hibernate properties definition --> </beans:bean> </beans:property> <beans:property name="persistenceUnitName" value="myPersistenceUnit" /> <beans:property name="packagesToScan"> <beans:list> <beans:value>com.creditease.bsettle.pay.model</beans:value> </beans:list> </beans:property> <beans:property name="loadTimeWeaver"> <beans:bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver" /> </beans:property> <beans:property name="jpaPropertyMap"> <beans:map> <beans:entry key="showSql" value="true"></beans:entry> <beans:entry key="generateDdl" value="false"></beans:entry> <beans:entry key="hibernate.format_sql" value="true"></beans:entry> <beans:entry key="hibernate.dialect" value="org.hibernate.dialect.Oracle10gDialect"></beans:entry> </beans:map> </beans:property> </beans:bean>于是把问题的怀疑点聚焦在web.xml初始化文件上,是否其中存在什么问题呢?
在web.xml中存在2个地方进行spring实例的初始化操作:
<context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:META-INF/spring-config.xml</param-value> </context-param>第二个位置是:
<servlet> <servlet-name>appServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:META-INF/applicationContext.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet>存在错误异常的加载顺序中,entityManagerFactory是在第二个位置中进行加载的。但是 OpenEntityManagerInViewFilter是否加载的顺序在其之前,然后就造成了这样的问题呢?
经过试验,果然是加载顺序的问题,将数据库加载的顺序提前即可。
5. 总结
基于上面的问题,我们可以看到, OpenEntityManagerInViewFilter是在系统启动过程中,优先被加载的,同时其对entityManagerFactory有依赖,就要求同时可以初始化entityManagerFactory的实例。
通过调整初始化的顺序,即可很好的修正上述的问题。
参考资料:
1. http://whoosh.iteye.com/blog/1300721