进退两难的Spring3+JPA2+Tomcat8

9 篇文章 0 订阅
7 篇文章 0 订阅

环境

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值