©
本文档使用
php.cn手册 发布
到目前为止本章讨论的一直是纯Spring AOP。在这一节里面我们将介绍如何使用AspectJ compiler/weaver 来代替Spring AOP或者作为它的补充,因为有些时候Spring AOP单独提供的功能也许并不能满足你的需要。
Spring提供了一个小巧的AspectJ aspect library,你可以在程序发行版本中单独使用
spring-aspects.jar
文件,并将其加入到classpath下以使用其中的切面。
第 6.8.1 节 “在Spring中使用AspectJ进行domain object的依赖注入”和第 6.8.2 节 “Spring中其他的AspectJ切面” 讨论了该库以及如何使用该库。
第 6.8.3 节 “使用Spring IoC来配置AspectJ的切面”讨论了如何对通过AspectJ compiler织入的AspectJ切面进行依赖注入。
最后第 6.8.4 节 “在Spring应用中使用AspectJ加载时织入(LTW)”介绍了使用AspectJ的Spring应用程序如何进行加载期织入(load-time weaving)。
Spring容器对application context中定义的bean进行实例化和配置。同样也可以通过bean factory
来为一个已经存在且已经定义为spring bean的对象应用所包含的配置信息。
spring-aspects.jar
中包含了一个annotation-driven的切面,
提供了能为任何对象进行依赖注入的能力。这样的支持旨在为
脱离容器管理而创建的对象进行依赖注入。领域对象经常处于这样的情形:
它们可能是通过new
操作符创建的对象,也可能是由ORM工具查询数据库所返回的结果。
@Configurable
注解标记了一个类可以通过Spring-driven方式来配置。
在最简单的情况下,我们只把它当作标记注解:
package com.xyz.myapp.domain;
import org.springframework.beans.factory.annotation.Configurable;
@Configurable
public class Account {
// ...
}
当只是简单地作为一个标记接口来使用的时候,Spring将采用和该已注解的类型
(比如Account
类)全名(com.xyz.myapp.domain.Account
)
一致的bean原型定义来配置一个新实例。由于一个bean默认的名字就是它的全名,
所以一个比较方便的办法就是省略定义中的id
属性:
<bean class="com.xyz.myapp.domain.Account" scope="prototype"> <property name="fundsTransferService" ref="fundsTransferService"/> </bean>
如果你希望明确的指定bean原型定义的名字,你可以在注解中直接定义:
package com.xyz.myapp.domain;
import org.springframework.beans.factory.annotation.Configurable;
@Configurable("account")
public class Account {
// ...
}
Spring会查找名字为"account"的bean定义,并使用它作定义来配置一个新的
Account
实例。
你也可以使用自动装配来避免手工指定原型定义的名字。只要设置@Configurable
注解中的autowire
属性就可以让Spring进行自动装配:
指定@Configurable(autowire=Autowire.BY_TYPE)
或者
@Configurable(autowire=Autowire.BY_NAME
可以让自动装配分别按照类型或名字进行。
作为另外一种选择,在Spring2.5中最好是在域或方法级使用@Autowired
和
@Resource
为你的@Configurable
beans指定
明确的、注解驱动的依赖注入。(详情请参看第 3.11 节 “基于注解(Annotation-based)的配置”)
最后,你可以通过使用dependencyCheck
属性,让Spring对新创建和配置的对象的对象引用进行
依赖检查(例如:@Configurable(autowire=Autowire.BY_NAME,dependencyCheck=true)
)。
如果这个属性设置为true,Spring会在配置结束后校验(除了primitives和collections类型)
所有的属性是否都被设置。
仅仅使用注解并没有做任何事情。但是spring-aspects.jar
中的AnnotationBeanConfigurerAspect
会在注解存在时起作用。实质上切面指明:
“在初始化一个由@Configurable
注解的新对象时,
Spring按照注解中的属性来配置这个新创建的对象”。这种情况下,initialization
指新初始化的(比如用new
初始化)的对象以及能进行反序列化的
Serializable
对象(例如通过
readResolve()方法)。
在上一段中一个关键的阶段就是“inessence”。多数情况下,“
当从一个新对象初始化返回之后”的精确语义很不错...这种语境下,
“初始化之后”的意思是依赖将在对象被构造之后注入 -
这意味着在类的构造器块中依赖将不可用。如果你希望它能在构造器代码块执行
之前被注入,并从而在构造器中使用它,
那么你需要在@Configurable
接口声明上做类似的定义:
@Configurable(preConstruction=true)
你可以在 AspectJ Programming Guide一书的附录中 找到更多有关在AspectJ中各种切面类型的语义信息。
要实现上述的操作,已注解的类型必须由AspectJ weaver来织入 - 你可以使用一个构建时的ant/maven任务来完成
(参见AspectJ Development Environment Guide)或者使用加载时织入(参见 第 6.8.4 节 “在Spring应用中使用AspectJ加载时织入(LTW)”)。
类AnnotationBeanConfigurerAspect
本身也需要Spring来配置(获得bean factory的引用,使用bean factory配置新的对象)。为此Spring的
context
命名空间
定义了一个非常方便的标签。只要简单的在application context配置中包含下面的内容。
<context:spring-configured/>
如果你使用DTD代替Schema,对应的定义如下:
<bean class="org.springframework.beans.factory.aspectj.AnnotationBeanConfigurerAspect" factory-method="aspectOf"/>
在切面配置完成之前创建的@Configurable
对象实例会导致在log中留下一个warning,并且任何对于该对象的配置都不会生效。
举一个例子,一个Spring管理配置的bean在被Spring初始化的时候创建了一个domain object。
对于这样的情况,你需要定义bean属性中的"depends-on"属性来手动指定该bean依赖于configuration切面。
<bean id="myService"
class="com.xzy.myapp.service.MyService"
depends-on="org.springframework.beans.factory.aspectj.AnnotationBeanConfigurerAspect">
<!-- ... -->
</bean>
提供@Configurable
支持的一个目的就是使得domain object的单元测试
可以独立进行,不需要通过硬编码查找各种倚赖关系。如果@Configurable
类型没有通过AspectJ织入,则在单元测试过程中注解不会起到任何作用,
测试中你可以简单的为对象的mock或者stub属性赋值,并且和正常情况一样去使用该对象。
如果@Configurable
类型通过AspectJ织入,
我们依然可以脱离容器进行单元测试,不过每次创建一个新的@Configurable
对象时都会看到一个warning,标示该对象没有被Spring配置。
AnnotationBeanConfigurerAspect
通过一个AspectJ singleton切面来实现对
@Configurable
的支持。一个singleton切面的作用域和一个
静态变量的作用域是一样的,那就是说,对于每一个classloader有一个切面来定义类型。
这就意味着如果你在一个classloader层次结构中定义了多个application context的时候就需要考虑
在哪里定义<aop:spring-configured/>
bean和在哪个classpath下
放置spring-aspects.jar
。
考虑一下典型的Spring web项目,一般都是由一个父application context定义大部分business service和
所需要的其他资源,然后每一个servlet拥有一个子application context定义。所有这些context共存于
同一个classloader体系下,因此AnnotationBeanConfigurerAspect
仅保持
一个对context的引用。在这样的情况下,我们推荐在父application context中定义
<aop:spring-configured/>
bean:这里所定义的service可能是
你希望注入domain object的。这样做的结果是你不能为子application context中
使用@Configurable的domain object配置bean引用(可能你也根本就不希望那么做!)。
当在一个容器中部署多个web-app的时候,请确保每一个web-application使用自己的classloader 来加载spring-aspects.jar中的类(例如将spring-aspects.jar放在WEB-INF/lib目录下)。 如果spring-aspects.jar被放在了容器的classpath下(因此也被父classloader加载),则所有的 web application将共享一个aspect实例,这可能并不是你所想要的。
除了@Configurable
切面,
spring-aspects.jar
包含了一个AspectJ切面可以用来为
那些使用了@Transactional
注解的类型和方法驱动Spring事务管理。
提供这个的主要目的是有些用户希望脱离Spring容器使用Spring的事务管理。
解析@Transactional
注解的切面是
AnnotationTransactionAspect
。当使用这个切面时,
你必须注解这个实现类(和/或这个类中的方法),而不是
这个类实现的接口(如果有)。AspectJ允许在接口上注解的Java规则 不被继承。
类之上的一个@Transactional
注解为该类中任何
public操作的执行指定了默认的事务语义。
类内部方法上的一个@Transactional
注解会覆盖类注解(如果存在)
所给定的默认的事务语义。具有public、protected和default修饰符的方法都可以被注解。
直接注解protected和default方法是让这个操作的执行获得事务划分的唯一途径。
对于AspectJ程序员,希望使用Spring管理配置和事务管理支持,不过他们不想(或者不能)使用注解,
spring-aspects.jar
也包含了一些抽象
切面供你继承来提供你自己的切入点定义。参见AbstractBeanConfigurerAspect
和AbstractTransactionAspect
的Javadoc获取更多信息。
作为一个例子,下面的代码片断展示了如何编写一个切面,然后通过和类全名匹配的bean原型定义来
配置domian object中定义的所有实例:
public aspect DomainObjectConfiguration extends AbstractBeanConfigurerAspect {
public DomainObjectConfiguration() {
setBeanWiringInfoResolver(new ClassNameBeanWiringInfoResolver());
}
// the creation of a new bean (any object in the domain model)
protected pointcut beanCreation(Object beanInstance) :
initialization(new(..)) &&
SystemArchitecture.inDomainModel() &&
this(beanInstance);
}
当在Spring application中使用AspectJ的时候,很自然的会想到用Spring来管理这些切面。 AspectJ runtime自身负责切面的创建,这意味着通过Spring来管理AspectJ 创建切面依赖于切面所使用的AspectJ instantiation model(per-clause)。
大多数AspectJ切面都是singleton切面。管理这些切面非常容易,
和通常一样创建一个bean定义引用该切面类型就可以了,并且在bean定义中包含
'factory-method="aspectOf"'
这个属性。
这确保Spring从AspectJ获取切面实例而不是尝试自己去创建该实例。示例如下:
<bean id="profiler" class="com.xyz.profiler.Profiler"
factory-method="aspectOf">
<property name="profilingStrategy" ref="jamonProfilingStrategy"/>
</bean>
non-singleton切面的配置稍难一点,然而它可以通过定义一个bean原型定义并且使用
spring-aspects.jar
中的@Configurable支持,
当切面实例由AspectJ runtime创建后进行配置。
如果你希望一些@AspectJ切面使用AspectJ来织入(例如使用load-time织入domain object)
而另一些@AspectJ切面使用Spring AOP,并且这些切面都由Spring来管理,那你就需要告诉Spring AOP
@AspectJ自动代理支持那些切面需要被自动代理。你可以通过在
<aop:aspectj-autoproxy>
声明中使用一个或多个
<include/>
元素。每个元素指定了一种命名格式,
只有bean命名至少符合其中一种情况下才会使用Spring AOP自动代理配置:
<aop:aspectj-autoproxy> <aop:include name="thisBean"/> <aop:include name="thatBean"/> </aop:aspectj-autoproxy>
不要被<aop:aspectj-autoproxy/>
元素的名字所误导:
用它会导致Spring AOP 代理的创建。在这中只是使用@AspectJ
类型的切面声明,但并不会涉及AspectJ运行时。
加载时织入(Load-time weaving(LTW))指的是在虚拟机载入字节码文件时动态织入AspectJ切面。 本 节关注于在Spring Framework中特的定context下配置和使用LTW:并没有LTW的介绍。 关于LTW和仅使用AspectJ配置LTW的详细信息(根本不涉及Spring),请查看 LTW section of the AspectJ Development Environment Guide。
Spring框架的值添加为AspectJ LTW在动态织入过程中提供了更细粒度的控制。使用Java(5+)的代理
能使用一个叫‘Vanilla’的AspectJ LTW,这需要在启动JVM的时候将某个VM参数设置为开。
这种JVM范围的设置在一些情况下或许不错,但通常情况下显得有些粗颗粒。而用Spring的LTW能让你在
per-ClassLoader
的基础上打开LTW,
这显然更加细粒度并且对“单JVM多应用”的环境更具意义(例如在一个典型应用服务器环境中一样)。
另外,在某些环境下,这能让你使用LTW而 不对应用服务器的启动脚本做任何改动,不然则需要添加 -javaagent:path/to/aspectjweaver.jar或者(以下将会提及的)-javaagent:path/to/spring-agent.jar。 开发人员只需简单修改应用上下文的一个或几个文件就能使用LTW,而不需依靠那些管理着部署配置 比如启动脚本的系统管理员。
经过以上讲解之后,先让我们来过一遍一个使用Spring的AspectJ LTW的快速示例,接着是一个 有对元素详细讲解的示例。如果想要一个完整的示例,请参看Petclinic(宠物诊所)的应用实例。
假设你是一个应用开人员,被指派诊断一个系统的若干性能问题。与其拿出性能分析工具, 我们不如开启一个简单的分析切面,使我们能很快地得到一些性能指标,这样我们就能马上 针对特定区域使用一些较细粒度的分析工具。
这就是一个分析切面。没什么特别的,只是一个快餐式的基于时间的模拟分析器, 使用类@AspectJ风格的切面声明。
package foo; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Pointcut; import org.springframework.util.StopWatch; import org.springframework.core.annotation.Order; @Aspect public class ProfilingAspect { @Around("methodsToBeProfiled()") public Object profile(ProceedingJoinPoint pjp) throws Throwable { StopWatch sw = new StopWatch(getClass().getSimpleName()); try { sw.start(pjp.getSignature().getName()); return pjp.proceed(); } finally { sw.stop(); System.out.println(sw.prettyPrint()); } } @Pointcut("execution(public * foo..*.*(..))") public void methodsToBeProfiled(){} }
我们还需要创建一个“META-INF/aop.xml
”文件,以告知AspectJ weaver
我们要把ProfilingAspect
织入到类中。这个文件惯例,即在Java classpath中
出现一个文件称作“META-INF/aop.xml
”是标准的AspectJ。
<!DOCTYPE aspectj PUBLIC "-//AspectJ//DTD//EN" "http://www.eclipse.org/aspectj/dtd/aspectj.dtd"> <aspectj> <weaver> <!-- only weave classes in our application-specific packages --> <include within="foo.*"/> </weaver> <aspects> <!-- weave in just this aspect --> <aspect name="foo.ProfilingAspect"/> </aspects> </aspectj>
现在来看Spring特定的配置部分。我们需要配置一个LoadTimeWeaver
(稍后会有解释,暂时不多深究)。当将一个或多个“META-INF/aop.xml
”文件中的切面
配置织入你的应用程序的类中时,这个加载时织入器是必须的。这样的好处是不需要很多的配置,
正如下面你看到的一样(还有另外一些参数供你指定,我们将在后面详细介绍)。
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd"> <!-- a service object; we will be profiling it's methods --> <bean id="entitlementCalculationService" class="foo.StubEntitlementCalculationService"/> <!-- this switches on the load-time weaving --> <context:load-time-weaver/> </beans>
现在万事俱备 - 切面,META-INF/aop.xml
文件,以及Spring的配置 -
让我们创建一个带有main(..)
方法的简单驱动类来演示LTW的作用吧。
package foo;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public final class Main {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml", Main.class);
EntitlementCalculationService entitlementCalculationService
= (EntitlementCalculationService) ctx.getBean("entitlementCalculationService");
// the profiling aspect is 'woven' around this method execution
entitlementCalculationService.calculateEntitlement();
}
}
最后还有一件事要做。此节之前的介绍说过可以有选择性的基于Spring的
per-ClassLoader
来启动LTW,而且的确如此。不过,对此例来说,
我们将使用Java代理(由Spring提供)来启动LTW。这个就是用以运行上面Main
类的命令行语句:
java -javaagent:C:/projects/foo/lib/global/spring-agent.jar foo.Main
-javaagent
是一个Java 5+标记,用来指定和激活
使JVM上的程序运行的代理。Spring框架装载了一个InstrumentationSavingAgent
代理,在上面的例子中被作为了-javaagent
参数的值打包在
spring-agent.jar
中。
Main
程序运行的输出如下所示。(我已经在
calculateEntitlement()
的实现中插入了Thread.sleep(..)
语句,以免让模拟分析器获取0毫秒 - 这里的01234
毫秒并非是AOP引入的系统开销。)
Calculating entitlement StopWatch 'ProfilingAspect': running time (millis) = 1234 ------ ----- ---------------------------- ms % Task name ------ ----- ---------------------------- 01234 100% calculateEntitlement
因为这个LTW使用成熟的AspectJ,我们并不局限于通知Spring beans的方法;接下来这个稍有变化的
Main
程序将生成同样的结果。
package foo;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public final class Main {
public static void main(String[] args) {
new ClassPathXmlApplicationContext("beans.xml", Main.class);
EntitlementCalculationService entitlementCalculationService =
new StubEntitlementCalculationService();
// the profiling aspect will be 'woven' around this method execution
entitlementCalculationService.calculateEntitlement();
}
}
注意以上程序我们只是引导了Spring容器,然后完全在Spring上下文之外创建了一个
StubEntitlementCalculationService
的实例...分析通知仍然得到织入。
上面的例子虽然简单了些,但Spring中基本的LTW支持都已介绍完了, 此节余下内容将对使用这些配置和用法背后的理由作详细解释。
类ProfilingAspect
在此例中虽然基本但是颇为有用。这是一个很好的开发时切面的例子,开发者可以在开发过程中使用它(废话),
然后也能从已部署到UAT或者生产环境的应用中轻易的脱离。
你在LTW中使用的切面必须是AspectJ切面。你可以使用AspectJ语言或者类@AspectJ风格来编写你的切面。 后一种方式当然只能在Java 5+中使用,但它说明了你的切面可以同时对AspectJ和Spring AOP切面有效。 此外,编译后的切面类需要被注册到classpath下。
AspectJ LTW的基础设施是用一个或多个位于Java classpath上的(可以是直接的文件形式,
也可以是更典型的jar包形式)META-INF/aop.xml
文件配置起来的。
有关文件的结构和内容都在AspectJ的参考文档中有详细介绍,有兴趣的读者
请参考这些资源。(很庆幸这一节比较简短,但aop.xml
文件
是100% AspectJ的 - 没有任何使用Spring特定的信息或语义,因此我也没有什么可贡献的。
与其重写这些已由AspectJ开发者提供的令人满意的章节,我不如领你到这里。)
你至少需要以下类库来让Spring框架支持AspectJ LTW:
spring.jar
(2.5或更高版本)
aspectjrt.jar
(1.5或更高版本)
aspectjweaver.jar
(1.5或更高版本)
如果你正在使用 由Spring提供的代理来激活检测(instrumentation)功能,你会需要:
spring-agent.jar
Spring LTW功能的关键组件是LoadTimeWeaver
接口
(在org.springframework.instrument.classloading
包中),
以及Spring分发包中大量的实现。LoadTimeWeaver
的实现负责
在运行时把一个或多个java.lang.instrument.ClassFileTransformers
类添加到
ClassLoader
中,这能产生各种各样有趣的应用,LTW切面恰好便是其中之一。
如果你对运行时类文件变换的思想还不熟悉,推荐你在继续之前阅读
java.lang.instrument
包的Javadoc API文档。
这其实并不难-反而有些恼人-因为有用的文件并不多...关键的接口和类都将会在此节呈现给你。
用XML为ApplicationContext
配置一个
LoadTimeWeaver
简单得只需要添加一行。
(请注意几乎肯定你需要使用ApplicationContext
作为你的
Spring容器 - 一般来说只有BeanFactory
是不够的,
因为LTW功能需要用到BeanFactoryPostProcessors
。)
当要使用Spring框架的LTW功能时,你需要配置一个LoadTimeWeaver
,
一般可以用<context:load-time-weaver/>
元素来完成。
下面为一个有效的使用默认设置的<context:load-time-weaver/>
定义。
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd"> <context:load-time-weaver/> </beans>
上面<context:load-time-weaver/>
bean的定义会自动为你定义和注册若干
特定LTW的基础设施beans,比如一个LoadTimeWeaver
和一个AspectJWeavingEnabler
。请注意
<context:load-time-weaver/>
是怎样在context
命名空间下被定义的;还要注意被引用的XML Schema文件只在Spring 2.5或更高版本中才可用。
上面的配置为你定义并注册了一个默认的LoadTimeWeaver
bean。
默认的LoadTimeWeaver
是一个
DefaultContextLoadTimeWeaver
类,它更倾向于去装饰一个能自动检测的LoadTimeWeaver
类:LoadTimeWeaver
的确切类型会根据你的运行时环境“自动检测”出来(概述如下表)。
表 6.1. DefaultContextLoadTimeWeaver
LoadTimeWeavers
DefaultContextLoadTimeWeaver
类和LoadTimeWeavers
接口
运行时环境 |
LoadTimeWeaver 的接口实现 |
---|---|
BEA's Weblogic 10环境下 |
|
Oracle's OC4J环境下 |
|
GlassFish环境下 |
|
以Spring
|
|
不过,我们更希望这些类加载器能遵循共同的规范
(例如适用 |
|
请注意当使用DefaultContextLoadTimeWeaver
时只有
LoadTimeWeavers
实现类能进行自动检测:
当然,你也可以通过指定将类的完全限定名作为<context:load-time-weaver/>
元素中weaver-class
属性的值
来指定究竟想使用哪个LoadTimeWeaver
的实现。如下例:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<context:load-time-weaver
weaver-class="org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver"/>
</beans>
在<context:load-time-weaver/>
元素上定义和注册的
LoadTimeWeaver
接口可以在Spring容器中以
loadTimeWeaver
名字找到。
记住LoadTimeWeaver
接口只是作为Spring LTW基础设施的一个机制
用来添加一个或多个ClassFileTransformers
的。
ClassFileTransformer
类实际是利用
ClassPreProcessorAgentAdapter
类(包含在
org.aspectj.weaver.loadtime
中)来进行LTW的。
有关ClassPreProcessorAgentAdapter
的细节请参见
类级别的javadoc,织入实际怎样生效的具体内容已经超出本节讨论范围。
让我们来讨论<context:load-time-weaver/>
的最后一个属性:
aspectj-weaving
。 这是一个简单的LTW开关,就这么简单。
它可以接受如下所述的三种值,如果不显示设置此属性则其默认值为autodetect
表 6.2. aspectj-weaving
属性值
属性值 | 注释 |
---|---|
|
AspectJ织入功能开启,切面将会在加载时适当时机被织入。 |
|
LTW功能关闭...不会在加载时织入切面。 |
|
如果Spring LTW基础设施能找到至少一个 |
这最后一节包括所有你在诸如应用服务器和web容器中使用Spring的LTW功能时需要的额外设置和配置。
你可能在各种Java应用中通过使用由Spring提供的检测代理启用Spring的LTW功能
(独立应用或者基于应用服务器的应用)。这样的话,可以通过指定
-javaagent:path/to/spring-agent.jar
选项来启动虚拟机。
请注意这需要修改虚拟机的启动脚本,但在某些应用服务器环境下是禁止这么做的
(这取决于你的操作策略)。
对于部署在Apache Tomcat 5.0或更高版本上的web应用,Spring将一个
TomcatInstrumentableClassLoader
注册成为web应用的类加载器。
必须的Tomcat设置如下所示,你可以把它放在Tomcat WAR包根目录下的核心文件
server.xml
中或放到应用特定的META-INF/context.xml
文件中。
Spring的spring-tomcat-weaver.jar
需要被包含到Tomcat
的common lib路径下以确保设置生效。
<Context path="/myWebApp" docBase="/my/webApp/location"> <Loader loaderClass="org.springframework.instrument.classloading.tomcat.TomcatInstrumentableClassLoader" useSystemClassLoaderAsParent="false"/> </Context>
注意:当使用LTW时,我们一般推荐使用Tomcat 5.5.20或更高版本。
先前的版本对定制的ClassLoader
设置会产生问题。
另外,请考虑使用在Tomcat启动脚本中(见上面)指定由Spring提供的通用虚拟机代理。 这样才能使检测功能在所有已部署的web应用中可用,无论其上运行的是哪种类加载器。
有关更多基于Tomcat织入设置的详细讨论,请参考讨论各种不同Tomcat版本内容的 第 12.6.1.3.1 节 “Tomcat(5.0以上)加载时的织入配置”一节。虽然本节主要关注于 JPA persistence提供者的设置,但也谈到了Tomcat各种特定设置适用于一般加载时织入的情况。
BEA WebLogic(版本10或更高),Oracle的JavaEE容器(OC4J 10.1.3.1或更高)以及
Resin(版本3.1或更高)提供具有本地检测能力的类加载器。
Srping的原生LTW利用这些类加载器来激活AspectJ织入。你可以通过简单地激活之前提到的
context:load-time-weaver
来启动LTW功能。具体来说,即你
不需要通过修改启动脚本来添加
-javaagent:path/to/spring-agent.jar
。
GlassFish同样也提供了检测能力的类加载器,不过只能在它的EAR环境下使用。 对于GlassFish的web应用,可以使用跟上面tomcat相同的设置。