到目前为止,我们在这一章中介绍的所有内容都是纯Spring AOP。在本节中,如果你要使用Spring AOP之外的功能,我们将介绍如何在Spring中使用AspectJ编译器或weaver。
Spring附带了一个小的AspectJ方面库,它在你的发行版中作为spring-aspects.jar单独提供。为了使用类路径中的方面,需要将其添加到类路径中。“使用aspectj来依赖注入带有Spring的域对象”,以及“aspectj的其他Spring方面”,讨论了这个库的内容以及如何使用它。“通过使用SpringIOC来配置AspectJ方面”讨论了如何使用AspectJ编译器对AspectJ方面进行依赖注入。最后,“在Spring框架中使用AspectJ进行加载时编织”,为使用AspectJ的Spring应用程序提供了加载时编织的介绍。
Spring容器实例化并配置在应用程序上下文中定义的bean。如果给定包含要应用的配置的bean定义的名称,也可以要求bean工厂配置预先存在的对象。spring-aspects.jar包含一个注解驱动的方面,它利用这个能力允许任何对象的依赖注入。该支持用于在任何容器的控制范围之外创建的对象。域对象通常属于此类,因为它们通常是通过new运算符或ORM工具作为数据库查询的结果以编程方式创建的。
@Configurable注解将类标记为符合Spring驱动配置。在最简单的情况下,可以将它纯粹用作标记注解,如下示例所示:
package com.xyz.myapp.domain;
import org.springframework.beans.factory.annotation.Configurable;
@Configurable
public class Account {
// ...
}
以这种方式用作标记接口时,Spring通过使用与完全限定类型名(com.xyz.myapp.domain.account)同名的bean定义(通常是原型范围)来配置注解类型(本例中为account)的新实例。由于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实例。
你还可以使用自动装载来避免必须指定一个专用的bean定义。要让Spring应用自动装载,请使用@Configurable注解的autowire属性。你可以分别指定@Configurable(autowire=Autowire.BY_TYPE)或@Configurable(autowire=Autowire.BY_NAME),用于按类型或按名称自动注解。作为替代方案,最好在字段或方法级别通过@Autowired或@Inject为@Configurable bean指定显式的、由注解驱动的依赖项注入(有关详细信息,请参阅基于注解的容器配置)。
最后,可以使用dependencyCheck属性(例如,@Configurable(autowire=Autowire.BY_NAME,dependencyCheck=true))对新创建和配置的对象中的对象引用启用Spring依赖性检查。如果将此属性设置为true,那么配置之后,Spring将验证是否已设置了所有属性(不是基元或集合)。
请注意,单独使用注解没有任何作用。Spring-Aspects.jar中的AnnotationBeanConfigurerAspect,才是让注解起作用的根本。本质上,aspect是,“从用@Configurable注解的类型的新对象的初始化返回后,根据注解的属性使用spring配置新创建的对象”。在此上下文中,“初始化”是指新实例化的对象(例如,用new运算符实例化的对象)以及正在进行反序列化的可序列化对象(例如,通过 readResolve())。
以上段落中的一个关键短语是“本质上”。在大多数情况下,“从新对象的初始化返回后”的确切语义是正确的。在此上下文中,“初始化后”意味着在构造对象之后注入依赖项。这意味着依赖项在类的构造函数体中不可用。如果希望在构造函数主体执行之前注入依赖项,从而在构造函数主体中可用,则需要在@Configurable声明中定义此依赖项,如下所示:
@Configurable(preConstruction = true)
你可以在《AspectJ编程指南》的本附录中找到关于AspectJ中各种切点类型的语言语义的更多信息。
要使这项工作,注解类型必须与AspectJ Weaver一起编织。你可以使用构建时Ant或Maven任务来执行此操作(例如,请参阅《AspectJ开发环境指南》)或加载时编织(请参阅Spring框架中的加载时编织与AspectJ)。AnnotationBeanConfigureAspect本身需要由Spring进行配置(以便获得用于配置新对象的bean工厂的引用)。如果使用基于Java的配置,可以将@EnableSpringConfigureds\添加到任何“@Configuration”类,如下:
@Configuration
@EnableSpringConfigured
public class AppConfig {
}
如果你更喜欢基于XML的配置,那么Spring上下文名称空间定义了一个方便的context:spring-configured的元素,你可以使用它,如下所示:
<context:spring-configured/>
配置方面之前创建的@Configurable objects实例会导致向调试日志发出消息,并且不会对对象进行任何配置。一个例子可能是Spring配置中的bean,它在由Spring初始化时创建域对象。在这种情况下,可以使用depends-on bean属性手动指定bean依赖于配置方面。以下示例显示如何使用“depends-on”属性:
<bean id="myService"
class="com.xzy.myapp.service.MyService"
depends-on="org.springframework.beans.factory.aspectj.AnnotationBeanConfigurerAspect">
<!-- ... -->
</bean>
不要通过bean配置器方面激活@Configurable,除非你真正打算在运行时依赖它的语义。特别是,确保在注册为容器中常规SpringBean的bean类上不使用@Configurable。这样做会导致双重初始化,一次通过容器,一次通过方面。
单元测试@Configurable对象
@Configurable支持的目标之一是能够对域对象进行独立的单元测试,而无需硬编码查找。如果@Configurable types不是由aspectj编织的,那么在单元测试期间注解没有影响。你可以在被测对象中设置mock或stub属性引用,并正常进行。如果@Configurable types已经被aspectj编织,你仍然可以像正常一样在容器外部进行单元测试,但是每次构造@Configurable对象时都会看到一条警告消息,表明它没有被spring配置。
和多个Application Contexts一起工作
用于实现@Configurable Support的AnnotationBeanConfigurerAspect是一个AspectJ单例方面。单个方面的范围与静态成员的范围相同:每个类加载器都有一个方面实例来定义类型。这意味着,如果在同一个类加载器层次结构中定义多个应用程序上下文,则需要考虑在何处定义@EnableSpringConfigured bean以及在何处将spring-aspects.jar放在类路径上。
考虑一个典型的SpringWeb应用程序配置,它具有一个定义公共业务服务的共享父应用程序上下文、支持这些服务所需的所有内容,以及每个servlet的一个子应用程序上下文(其中包含特定于该servlet的定义)。所有这些上下文都存在于同一个类加载器层次结构中,因此AnnotationBeanConfigurerAspect只能包含对其中一个上下文的引用。在这种情况下,我们建议在共享(父)应用程序上下文中定义@EnableSpringConfigured bean。这定义了你可能希望注入到域对象中的服务。结果是,你无法使用@Configurable机制(这可能不是你想要做的事情)配置域对象,因为它引用了在子(servlet特定)上下文中定义的bean。
在同一容器中部署多个Web应用程序时,请确保每个Web应用程序都使用自己的类加载器(例如,通过将spring-aspects.jar放在“WEB-INF/lib”中)加载spring-aspects.jar中的类型。如果Spring-Aspects.jar只添加到容器范围的类路径(因此由共享父类加载器加载),那么所有Web应用程序都共享相同的Aspect实例(这可能不是你想要的)。
除了@Configurable方面,spring-aspects.jar还包含一个aspectj方面,你可以使用它来驱动spring对用@transactional annotation注解的类型和方法的事务管理。这主要面向希望在Spring容器之外使用Spring框架的事务支持的用户。
解释@transactional注解的方面是AnnotationTransactionAspect。使用此方面时,必须注解实现类(或该类中的方法或两者都使用),而不是类实现的接口(如果有的话)。AspectJ遵循Java的规则,接口上的注解不是继承的。
类上的@transactional注解指定执行类中任何公共操作的默认事务语义。
类内方法的@transactional注解将覆盖类注解(如果存在)给出的默认事务语义。任何可见性的方法都可以进行注解,包括私有方法。直接注解非公共方法是实现此类方法的事务划分的唯一方法。
自SpringFramework4.2以来,spring-aspects提供了类似的方面,为标准javax.transaction.transactional注解提供了完全相同的特性。有关详细信息,请查看JtaAnnotationTransactionAspect。
对于那些希望使用Spring配置和事务管理支持但不希望(或不能)使用注解的AspectJ程序员,spring-aspects.jar还包含可以扩展以提供自己的切入点定义的抽象方面。有关更多信息,请参阅AbstractBeanConfigureAspect和AbstractTransactionspect方面的源代码。作为一个例子,下面的摘录展示了如何通过使用与完全限定类名匹配的原型bean定义来编写一个方面来配置域模型中定义的对象的所有实例:
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);
}
当你将AspectJ方面与Spring应用程序一起使用时,很自然地希望并期望能够使用Spring配置这些方面。AspectJ运行时本身负责方面的创建,通过Spring配置AspectJ创建的方面的方法取决于方面使用的AspectJ实例化模型(per-xxx子句)。
AspectJ方面的大多数是单体方面。这些方面的配置很容易。你可以创建一个bean定义,该定义将方面类型作为普通类型引用,并包含factory-method="aspectOf" bean属性。这样可以确保Spring通过向AspectJ请求来获得AspectJ实例,而不是试图创建实例本身。以下示例显示如何使用factory-method="aspectOf"属性:
<bean id="profiler" class="com.xyz.profiler.Profiler"
factory-method="aspectOf">
<property name="profilingStrategy" ref="jamonProfilingStrategy"/>
</bean>
非单例方面更难配置。但是,可以通过创建原型bean定义和使用spring-aspects.jar中的@Configurable支持来配置Aspectj运行时创建的bean之后的方面实例来实现这一点。
如果你有一些@Aspectj方面要用aspectj编织(例如,对域模型类型使用加载时编织)和其他@Aspectj方面要用spring aop,并且这些方面都在spring中配置,那么你需要告诉spring aop @Aspectj auto-proxing支持在配置中定义的@Aspectj方面的哪个确切子集应用于自动代理。可以通过在<aop:aspectj-autoproxy/>声明中使用一个或多个<include/>元素来完成此操作。每个<include/>元素都指定一个名称模式,只有名称与至少一个模式匹配的bean才用于SpringAOP自动代理配置。以下示例显示如何使用<include/>元素:
<aop:aspectj-autoproxy>
<aop:include name="thisBean"/>
<aop:include name="thatBean"/>
</aop:aspectj-autoproxy>
不要被<aop:aspectj-autoproxy/>元素的名称误导。使用它可以创建SpringAOP代理。这里使用的是Aspect声明的@AspectJ样式,但不涉及AspectJ运行时。
加载时间编织(LTW)指的是在将AspectJ方面加载到Java虚拟机(JVM)中的过程中,将AspectJ方面编织成应用程序的类文件。本节的重点是在Spring框架的特定上下文中配置和使用LTW。本节不是LTW的一般介绍。有关LTW的详细信息和仅使用AspectJ配置LTW(根本不涉及Spring),请参阅AspectJ开发环境指南的LTW部分。
Spring框架为AspectJ LTW带来的价值在于能够对编织过程进行更细粒度的控制。“Vanilla 的AspectJ LTW是通过使用Java(5 +)代理来实现的,它在启动JVM时通过指定VM参数来打开。因此,它是一个JVM范围的设置,在某些情况下可能很好,但通常有点过于粗糙。启用了Spring的LTW允许你在每个类加载器的基础上打开LTW,它更细粒度,并且在“单个JVM多应用程序”环境中(例如在典型的应用程序服务器环境中)更有意义。
此外,在某些环境中,加载时编织,不需要对应用程序服务器的启动脚本进行任何修改,这些脚本需要添加-javaagent:path/to/aspectjweaver.jar或(如本节后面所述)javaagent:path/to/spring-instrument.jar。开发人员配置应用程序上下文以启用加载时编织,而不是依赖通常负责部署配置(如启动脚本)的管理员。
既然销售宣传已经结束,那么让我们先浏览一下使用Spring的AspectJ LTW的一个快速示例,然后详细介绍示例中介绍的元素。有关完整的示例,请参阅PetClinic示例应用程序。
一个例子
假设你是一个应用程序开发人员,负责诊断系统中某些性能问题的原因。我们将打开一个简单的分析方面,让我们快速获得一些性能指标,而不是开发一个分析工具。然后我们可以在之后立即对特定区域应用更细粒度的分析工具。
这里的示例使用XML配置。你还可以配置和使用Java配置的AspectJ。具体来说,你可以使用@EnableLoadTimeWeaving注解作为<context:load-time-weaver/>的替代方法(有关详细信息,请参阅下面)。
下面的示例显示了分析方面,这是不理想的。它是一个基于时间的探查器,使用@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类路径上的文件(或文件)的存在,称为META-IMF/aop.xml是标准AspectJ。下面的示例显示aop.xml文件:
<!DOCTYPE aspectj PUBLIC "-//AspectJ//DTD//EN" "https://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(稍后解释)。这个加载时weaver是负责将一个或多个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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!-- a service object; we will be profiling its 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在每个类加载器的基础上选择性地打开LTW,这是正确的。但是,对于这个例子,我们使用Java代理(用Spring提供)来切换LTW。我们使用以下命令来运行前面显示的主类:
java -javaagent:C:/projects/foo/lib/global/spring-instrument.jar foo.Main
-javaagent是一个标志,用于指定和使代理能够检测在JVM上运行的程序。Spring框架附带这样一个代理,即InstrumentationSavingAgent,它打包在Spring-instrument.jar中,在前面的示例中作为-javaagent参数的值提供。
主程序执行时的输出类似于下一个示例。(我在calculateEntitlement()实现中引入了一个Thread.sleep(..)语句,以便探查器实际捕获0毫秒以外的内容(01234毫秒不是AOP引入的开销)。下面的列表显示了运行分析器时得到的输出:
Calculating entitlement
StopWatch 'ProfilingAspect': running time (millis) = 1234
------ ----- ----------------------------
ms % Task name
------ ----- ----------------------------
01234 100% calculateEntitlement
由于此LTW是通过使用全AspectJ来实现的,因此我们不仅限于advising SpringBeans。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的新实例。分析advice仍然被编织在一起。
诚然,这个例子是简单的。但是,在前面的示例中已经介绍了在Spring中LTW支持的基础知识,本节的其余部分详细解释了每一位配置和使用背后的“为什么”。
本例中使用的ProfilingAspect可能是基本的,但非常有用。这是一个很好的开发时间方面的例子,开发人员可以在开发期间使用它,然后很容易地将其从部署到UAT或生产中的应用程序的构建中排除。
Aspects
你在LTW中使用的方面必须是AspectJ方面。你可以用aspectj语言本身编写它们,也可以用@Aspectj样式编写方面。你的方面是有效的AspectJ和SpringAOP方面。此外,编译的方面类需要在类路径上可用。
'META-INF/aop.xml'
AspectJ LTW基础结构通过使用Java类路径上的一个或多个META-INF/aop.xml 文件来配置(直接或更典型地,在JAR文件中)。
此文件的结构和内容在AspectJ参考文档的LTW部分中有详细说明。因为aop.xml文件是100%aspectj,所以我们在这里不再详细描述它。
Required libraries (JARS)
至少,你需要以下库来使用Spring框架对AspectJ LTW的支持:
如果使用Spring提供的代理来启用检测,还需要:
Spring配置
Spring LTW支持中的关键组件是loadTimeweaver接口(在org.springframework.instrument.classloading包中),以及与Spring发行版一起提供的许多实现。loadtimeweaver负责在运行时向类加载器添加一个或多个java.lang.instrument.ClassFileTransformers,这为各种有趣的应用程序打开了大门,其中一个应用程序恰好是方面的LTW。
如果你不熟悉运行时类文件转换的概念,请参阅java.lang.instrument包的javadoc API文档,然后继续。虽然该文档并不全面,但至少你可以看到关键的接口和类(供你阅读本节时参考)。
为特定的应用程序上下文配置LoadTimeWeaver与添加一行一样简单。(请注意,你几乎肯定需要使用ApplicationContext作为Spring容器-通常,BeanFactory是不够的,因为LTW支持使用BeanFactoryPostProcessors。)
要启用Spring框架的LTW支持,你需要配置一个LoadTimeWeaver,通常通过使用@EnableLoadTimeWeaving注解完成,如下所示:
@Configuration
@EnableLoadTimeWeaving
public class AppConfig {
}
或者,如果你更喜欢基于XML的配置,请使用<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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:load-time-weaver/>
</beans>
前面的配置自动为你定义和注册许多LTW特定的基础结构bean,例如LoadTimeWeaver和AspectJWeavingEnabler。默认的LoadTimeWeaver是DefaultContextLoadTimeWeaver类,它尝试修饰自动检测到的LoadTimeWeaver。“自动检测”的LoadTimeWeaver的确切类型取决于运行时环境。下表总结了各种LoadTimeWeaver实现:
运行时环境 | LoadTimeWeaver实现 |
---|---|
Running in Apache Tomcat | TomcatLoadTimeWeaver |
Running in GlassFish (limited to EAR deployments) | GlassFishLoadTimeWeaver |
Running in Red Hat’s JBoss AS or WildFly | JBossLoadTimeWeaver |
Running in IBM’s WebSphere | WebSphereLoadTimeWeaver |
Running in Oracle’s WebLogic | WebLogicLoadTimeWeaver |
JVM started with Spring InstrumentationSavingAgent (java -javaagent:path/to/spring-instrument.jar) | InstrumentationLoadTimeWeaver |
回退,期望底层类加载器遵循公共约定(即addTransformer和可选的getThrowawayClassLoader方法) | ReflectiveLoadTimeWeaver |
请注意,该表仅列出使用DefaultContextLoadTimeWeaver时自动检测的LoadTimeWeaver。你可以准确地指定要使用的LoadTimeWeaver实现。
若要指定具有Java配置的特定LoadTimeWeaver,请执行LoadTimeWeavingConfigurer接口并重写getLoadTimeWeaver()方法。以下示例指定了ReflectiveLoadTimeWeaver:
@Configuration
@EnableLoadTimeWeaving
public class AppConfig implements LoadTimeWeavingConfigurer {
@Override
public LoadTimeWeaver getLoadTimeWeaver() {
return new ReflectiveLoadTimeWeaver();
}
}
如果使用基于XML的配置,则可以将完全限定的类名指定为<context:load-time-weaver/>元素上weaver-class属性的值。同样,下面的示例指定了ReflectiveLoadTimeWeaver:
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:load-time-weaver
weaver-class="org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver"/>
</beans>
配置定义和注册的LoadTimeWeaver稍后可以使用众所周知的名称LoadTimeWeaver从Spring容器中检索。记住,LoadTimeWeaver仅作为Spring的LTW基础结构添加一个或多个ClassFileTransformer的机制存在。执行LTW的实际ClassFileTransformer是ClassPreprocessorAgentAdapter(来自org.aspectj.weaver.loadtime包)类。有关进一步的详细信息,请参见ClassPreprocessorAgentAdapter类的类级JavaDoc,因为实际影响编织的细节超出了本文档的范围。
还有一个最后要讨论的配置属性:aspectjWeaving属性(如果使用XML,则为 aspectj-weaving属性)。此属性控制是否启用LTW。它接受三个可能值中的一个,如果属性不存在,则默认值为autodetect。下表总结了三个可能的值:
Annotation Value | XML Value | Explanation |
---|---|---|
ENABLED | on | Aspectj编织已启用,并且各个方面在加载时视情况进行编织。 |
DISABLED | off | LTW关闭。加载时不编织任何方面。 |
AUTODETECT | autodetect | 如果SpringLTW基础结构可以找到至少一个META-INF/aop.xml文件,那么aspectj weaving就打开了。否则,它将关闭。这是默认值。 |
环境特定配置
最后一部分包含在应用程序服务器和Web容器等环境中使用Spring的LTW支持时所需的任何其他设置和配置。
Tomcat, JBoss, WebSphere, WebLogic
Tomcat、JBoss/WildFly、IBM WebSphere Application Server和Oracle WebLogic Server都提供了一个通用的应用程序类加载器,能够进行本地检测。Spring的本地LTW可以利用这些类加载器实现来提供AspectJ编织。如前所述,你可以简单地启用加载时编织。具体来说,你不需要修改jvm启动脚本来添加-javaagent:path/to/spring-instrument.jar。
注意,在jboss上,你可能需要禁用应用服务器扫描,以防止它在应用程序实际启动之前加载类。一个快速的解决方法是在工件中添加一个名为WEB-INF/jboss-scanning.xml的文件,其中包含以下内容:
<scanning xmlns="urn:jboss:scanning:1.0"/>
通用Java应用程序
当特定的loadtimeweaver实现不支持的环境中需要类检测时,JVM代理是通用的解决方案。对于这种情况,Spring提供了InstrumentationLoadTimeweaver,它需要特定于Spring(但非常通用)的JVM代理spring-instrument.jar,由common @EnableLoadTimeWeaving和<context:load-time-weaver/>设置自动检测。
要使用它,必须通过提供以下JVM选项来使用Spring代理启动虚拟机:
-javaagent:/path/to/spring-instrument.jar
请注意,这需要修改JVM启动脚本,这可能会阻止你在应用程序服务器环境中使用该脚本(取决于你的服务器和操作策略)。也就是说,对于每个JVM部署一个应用程序(如独立的Spring引导应用程序),你通常在任何情况下都控制整个JVM设置。