Spring JPA same class gets loaded twice by different class loaders

来源:互联网 发布:软件系统开发计划 编辑:程序博客网 时间:2024/06/05 22:38

最近在看《Spring In Action》,在做测试时,碰到了一个问题,那就是:

同一个Class,却被不同的Class Loader 加载,出现的异常为:

java.lang.ClassCastException: spring.in.action.bean.chapter05.OrderDetail cannot be cast to spring.in.action.bean.chapter05.OrderDetail.


Spring的配置文件内容如下:


<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd   http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd   http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd"><bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"><property name="locations"><list><!-- <value>classpath:/*/*/datasource.properties</value> --><value>F:\Eclipse2\spring-in-action\config\datasource.properties</value></list></property></bean><bean id="dataSource" class="org.apache.tomcat.dbcp.dbcp.BasicDataSource"><property name="driverClassName" value="${datasource.driverClassName}"></property><property name="url" value="${datasource.url}"></property><property name="username" value="${datasource.username}"></property><property name="password" value="${datasource.password}"></property><property name="maxActive" value="5"></property><property name="minIdle" value="3"></property></bean><bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"><property name="dataSource" ref="dataSource"></property><property name="jpaVendorAdapter"><bean class="org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter"><property name="database" value="MYSQL"></property><property name="showSql" value="true"></property><property name="generateDdl" value="true"></property></bean></property> <property name="jpaProperties"><props><prop key="eclipselink.ddl-generation">NONE</prop></props></property><property name="loadTimeWeaver"><bean class="org.springframework.instrument.classloading.SimpleLoadTimeWeaver"></bean></property></bean><bean id="orderDetailDao" class="spring.in.action.bean.chapter05.OrderDetailDaoImpl"><property name="entityManagerFactory" ref="entityManagerFactory"></property></bean></beans>



首先在网上搜了下,说是同一个Class,被不同的Class Loader 加载,所以会出现 ClassCastException。
然后,具体的将各个Class的ClassLoader打印出来看了下,发现真的不一样:

List<OrderDetail> orderDetailList = getJpaTemplate().find("select orderDetail from OrderDetail orderDetail");if(!orderDetailList.isEmpty()){for(Object obj : orderDetailList){System.out.println("Element:"+obj.getClass().getClassLoader());System.out.println("order:"+OrderDetail.class.getClassLoader());break;}}else{System.out.println("orderDetail list is null");}

结果是:

Element: org.springframework.instrument.classloading.SimpleInstrumentableClassLoader@15c07d8order: sun.misc.Launcher$AppClassLoader@19821f



从结果可以看出,一个OrderDetail用的是sun提供的ClassLoader,另一个用的是Spring的,而Spring这个明显是从Sun的ClassLoader中继承过来的。
所以解决方法是重写 SimpleLoadTimeWeaver:


public class MyLoadTimeWeaver extends SimpleLoadTimeWeaver {@Overridepublic ClassLoader getInstrumentableClassLoader() {// TODO Auto-generated method stubreturn super.getInstrumentableClassLoader().getParent();//return super.getInstrumentableClassLoader();}}


然后将配置文件中的LoadTimeWeaver属性的值变成:
<property name="loadTimeWeaver"><bean class="spring.in.action.bean.util.MyLoadTimeWeaver"></bean></property>

这样问题就解决了。


最后的Spring配置文件变为:

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd   http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd   http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd"><bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"><property name="locations"><list><!-- <value>classpath:/*/*/datasource.properties</value> --><value>F:\Eclipse2\spring-in-action\config\datasource.properties</value></list></property></bean><bean id="dataSource" class="org.apache.tomcat.dbcp.dbcp.BasicDataSource"><property name="driverClassName" value="${datasource.driverClassName}"></property><property name="url" value="${datasource.url}"></property><property name="username" value="${datasource.username}"></property><property name="password" value="${datasource.password}"></property><property name="maxActive" value="5"></property><property name="minIdle" value="3"></property></bean><bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"><property name="dataSource" ref="dataSource"></property><property name="jpaVendorAdapter"><bean class="org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter"><property name="database" value="MYSQL"></property><property name="showSql" value="true"></property><property name="generateDdl" value="true"></property></bean></property> <property name="jpaProperties"><props><prop key="eclipselink.ddl-generation">NONE</prop></props></property><property name="loadTimeWeaver"><bean class="spring.in.action.bean.util.MyLoadTimeWeaver"></bean></property></bean><bean id="orderDetailDao" class="spring.in.action.bean.chapter05.OrderDetailDaoImpl"><property name="entityManagerFactory" ref="entityManagerFactory"></property></bean></beans>



===========================================================================================================
那可不可以不用SimpleLoadTimeWeaver呢?因为用了它,我们就会改变Class的ClassLoader,造成同一个Class会被不同的ClassLoader加载。试了下在EntityManagerFactory的bean中,去掉loadtimeWeaver属性,得到的却是另一个异常:
java.lang.IllegalStateException: Cannot apply class transformer without LoadTimeWeaver specified

这个问题没有去研究,希望有研究的朋友,可以分享下

	
				
		
原创粉丝点击