环境
jdk:1.8
tomcat:8.0.42
jpa:2.1 & eclipselink jpa 2.6.3
spring:3.2.17
配置文件
pom.xml
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-web-api</artifactId>
<version>7.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>net.htage</groupId>
<artifactId>core</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- jstl -->
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- bean validate-->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.4.0.Final</version>
</dependency>
<dependency>
<groupId>org.glassfish.web</groupId>
<artifactId>el-impl</artifactId>
<version>2.2</version>
</dependency>
<!-- spring slf -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring-framework.version}</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>${slf4j.version}</version>
</dependency>
<!-- Logging with SLF4J & Log4J -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
<type>jar</type>
</dependency>
<!-- spring framework -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring-framework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring-framework.version}</version>
</dependency>
<!-- -javaagent/InstrumentationLoadTimeWeaver -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-instrument</artifactId>
<version>${spring-framework.version}</version>
</dependency>
<!-- Context/loader /ReflectiveLoadTimeWeaver -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-instrument-tomcat</artifactId>
<version>${spring-framework.version}</version>
</dependency>
<!-- orm -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring-framework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${spring-framework.version}</version>
</dependency>
<!-- jpa & eclipselink jpa -->
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>eclipselink</artifactId>
<version>2.6.3</version>
</dependency>
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>javax.persistence</artifactId>
<version>2.1.0</version>
</dependency>
<!-- java unit test -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring-framework.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
spring applicationContext.xml
<beans ...>
<!-- 不需要propertyConfigurer maven也可以为它赋值-->
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource"
p:driverClassName="{jdbc.driverClassName}"
p:url="{jdbc.url}"
p:username="{jdbc.username}"
p:password="{jdbc.password}" />
<!-- 定义扫描根路径为leot.test,不使用默认的扫描方式 -->
<context:component-scan base-package="net.htage.core" use-default-filters="false">
<!-- 扫描符合@Service @Repository的类 -->
<context:include-filter type="annotation" expression="org.springframework.stereotype.Service" />
<context:include-filter type="annotation" expression="org.springframework.stereotype.Repository" />
</context:component-scan>
<!-- ADD PERSISTENCE SUPPORT HERE (jpa, hibernate, etc) -->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="{pu}" />
<property name="dataSource" ref="dataSource" />
<property name="persistenceXmlLocation" value="classpath:META-INF/persistence.xml" />
<property name="loadTimeWeaver">
<bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver"/>
</property>
<property name="jpaVendorAdapter">
<bean id="jpaAdapter" class="org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter">
<property name="generateDdl" value="true" />
<property name="showSql" value="true"/>
<property name="database" value="MYSQL" />
<property name="databasePlatform" value="org.eclipse.persistence.platform.database.MySQLPlatform" />
</bean>
</property>
<property name="jpaDialect">
<bean class="org.springframework.orm.jpa.vendor.EclipseLinkJpaDialect" />
</property>
</bean>
<!-- 事务管理器 -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<!-- 事务注解扫描 -->
<tx:annotation-driven/>
</beans>
junit测试没问题
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.19.1</version>
<dependencies>
<dependency>
<groupId>org.apache.maven.surefire</groupId>
<artifactId>surefire-junit4</artifactId>
<version>2.19.1</version>
</dependency>
</dependencies>
<configuration>
<argLine>-Dfile.encoding=${project.build.sourceEncoding}</argLine>
<argLine>-javaagent:${settings.localRepository}/org/springframework/spring-instrument/${spring-framework.version}/spring-instrument-${spring-framework.version}.jar</argLine>
</configuration>
</plugin>
tomcat8中一路异常:
20-Apr-2017 13:29:19.991 严重 [localhost-startStop-1] org.apache.catalina.core.StandardContext.listenerStart Exception sending context initialized event to listener instance of class org.springframework.web.context.ContextLoaderListener
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [applicationContext.xml]: Invocation of init method failed; nested exception is java.lang.reflect.UndeclaredThrowableException
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1514)
... 32 more
Caused by: java.lang.IllegalStateException: Must start with Java agent to use InstrumentationLoadTimeWeaver. See Spring documentation.
at org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver.addTransformer(InstrumentationLoadTimeWeaver.java:87)
at org.springframework.orm.jpa.persistenceunit.SpringPersistenceUnitInfo.addTransformer(SpringPersistenceUnitInfo.java:111)
... 37 more
没办法看文档
1.JVM started with Spring InstrumentationSavingAgent
(java -javaagent:path/to/spring-instrument.jar)
InstrumentationLoadTimeWeaver
2.Fallback, expecting the underlying ClassLoader to follow common conventions (e.g. applicable to TomcatInstrumentableClassLoader and Resin)
ReflectiveLoadTimeWeaver
在手册的第9章: 9.1. DefaultContextLoadTimeWeaver LoadTimeWeavers
一难
1.applicationContext.xml搞上试试:
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="{pu}" />
<property name="dataSource" ref="dataSource" />
<property name="persistenceXmlLocation" value="classpath:META-INF/persistence.xml" />
<property name="loadTimeWeaver">
<bean class="org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver" />
</property>
...
</bean>
2.{项目名}\src\main\webapp\META-INF\context.xml
<?xml version="1.0" encoding="UTF-8"?>
<Context antiResourceLocking="true" path="/portal">
<Loader loaderClass="org.springframework.instrument.classloading.tomcat.TomcatInstrumentableClassLoader"/>
</Context>
3.{CATALINA_HOME}\lib下加入
spring-instrument-tomcat-{spring-framework.version}.jar
4.tomcat8跑起来了
二难
在netbeans中试试junit测试
2017-04-20 17:18:37,698 WARN [main] support.GenericApplicationContext (AbstractApplicationContext.java:487) - Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [applicationContext.xml]: Cannot create inner bean 'org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver#5ef60048' of type [org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver] while setting bean property 'loadTimeWeaver'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver#5ef60048' defined in class path resource [applicationContext.xml]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver]: Constructor threw exception; nested exception is java.lang.IllegalStateException: ClassLoader [sun.misc.Launcher$AppClassLoader] does NOT provide an 'addTransformer(ClassFileTransformer)' method.
2017-04-20 17:18:37,712 ERROR [main] context.TestContextManager (TestContextManager.java:324) - Caught exception while allowing TestExecutionListener [org.springframework.test.context.support.DependencyInjectionTestExecutionListener@3d285d7e] to prepare test instance [net.htage.core.web.test.CategoryTest@239963d8]
java.lang.IllegalStateException: Failed to load ApplicationContext
at org.springframework.test.context.CacheAwareContextLoaderDelegate.loadContext(CacheAwareContextLoaderDelegate.java:103)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [applicationContext.xml]: Cannot create inner bean 'org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver#5ef60048' of type [org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver] while setting bean property 'loadTimeWeaver'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver#5ef60048' defined in class path resource [applicationContext.xml]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver]: Constructor threw exception; nested exception is java.lang.IllegalStateException: ClassLoader [sun.misc.Launcher$AppClassLoader] does NOT provide an 'addTransformer(ClassFileTransformer)' method.
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveInnerBean(BeanDefinitionValueResolver.java:287)
... 26 more
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver#5ef60048' defined in class path resource [applicationContext.xml]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver]: Constructor threw exception; nested exception is java.lang.IllegalStateException: ClassLoader [sun.misc.Launcher$AppClassLoader] does NOT provide an 'addTransformer(ClassFileTransformer)' method.
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1039)
... 44 more
Caused by: org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver]: Constructor threw exception; nested exception is java.lang.IllegalStateException: ClassLoader [sun.misc.Launcher$AppClassLoader] does NOT provide an 'addTransformer(ClassFileTransformer)' method.
at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:163)
... 50 more
什么鬼?加上javaagent吗?
两全其美
两全其美的答案在哪?上下文范围内的加载时织入配置
applicationContext.xml
<!-- 自动检测织入器 -->
<context:load-time-weaver/>
<bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="{pu}" />
<property name="dataSource" ref="dataSource" />
<property name="persistenceXmlLocation" value="classpath:META-INF/persistence.xml" />
<!-- 删除具体的织入方案-->
<property name="jpaVendorAdapter">
<bean id="jpaAdapter" class="org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter">
<property name="generateDdl" value="true" />
<property name="showSql" value="true"/>
<property name="database" value="MYSQL" />
<property name="databasePlatform" value="org.eclipse.persistence.platform.database.MySQLPlatform" />
</bean>
</property>
<property name="jpaDialect">
<bean class="org.springframework.orm.jpa.vendor.EclipseLinkJpaDialect" />
</property>
</bean>
junit单元测试时在pom.xml中加入javaagent.现在开发和布署都正常工作,tomcat8也没有启动异常,这又是什么鬼?
不是spring管理织入方案吗?我提供了TomcatInstrumentableClassLoader.怎么会织入失败?理解错了吗?
使用javaagent作个junit测试试一试
pom.xml
-javaagent:${settings.localRepository}/org/springframework/spring-instrument/${spring-framework.version}/spring-instrument-${spring-framework.version}.jar
ArticleServiceTest.java
public class ArticleServiceTest extends AbstractTests implements ApplicationContextAware{
private ApplicationContext applicationContext;
private CoreWebArticleService articleService;
@Override
public void setApplicationContext(ApplicationContext ac) throws BeansException {
this.applicationContext=ac;
}
@Before
public void init(){
articleService=applicationContext.getBean(CoreWebArticleService.class);
}
@Test
public void test(){
Page<Article> all=articleService.getNews(1,50);
Collection<Article> rs=all.getResult();
Iterator<Article> it=rs.iterator();
while(it.hasNext()){
Article a=it.next();
System.out.println("---------------------------------------------");
System.out.println("title:"+a.getTitle()+",author:"+a.getUser().getNickname()+"@"+a.getPublish());
System.out.println(a.getDescript());
System.out.println("category:"+a.getType().getName());
System.out.println("---------------------------------------------");
}
}
}
这个查询中不需要article_content,它又是延迟加载的目标,可见不是spring-instrument-xx.jar管理延迟加载织入.
persistence.xml
<property name="eclipselink.weaving.lazy" value="true"/>
persistence.xml
<property name="eclipselink.weaving.lazy" value="false"/>
spring不负责织入延迟加载
所以配了TomcatInstrumentableClassLoader也会提示:
reverting the lazy setting on the onetoone or manytoone
最终的配置如下:
1.pom.xml 的junit测试还是用: -javagent spring-instrument-xx.jar
2.war下的META-INF中context.xml中删除
<Loader loaderClass="org.springframework.instrument.classloading.tomcat.TomcatInstrumentableClassLoader"/>
3.spring applicationContext.xml中使用
<context:load-time-weaver/>
emf bean不配置具体的织入属性
4.tomcat8的bin\catalina.bat加入javaagent=eclipselink.jar
set JAVA_OPTS=%JAVA_OPTS% -javaagent:%CATALINA_HOME%\lib\eclipselink-2.5.2.jar
5.附注
本文说的是eclipselink jpa动态织入:
Configuring Dynamic Weaving
关于静态织入
Configuring Static Weaving
eclipselink.weaving.lazy=true只有在eclipselink.weaving=true和static时才起作用,true代表动态织入,static代表静态织入
You can use this extension only if weaving is configured to true or static. See “weaving” for more information.
http://www.eclipse.org/eclipselink/documentation/2.6/jpa/extensions/persistenceproperties_ref.htm#BABDBIFE
参考:
spring LTW agent方式在tomcat 8下无效
JPA_Spring Framework
Java Persistence API (JPA) Extensions Reference for EclipseLink, Release 2.6