对于最新的稳定版本,请使用 Spring Framework 6.2.10! |
将 AspectJ 与 Spring 应用程序一起使用
到目前为止,我们在本章中介绍的所有内容都是纯 Spring AOP。在本节中, 我们看看如何使用 AspectJ 编译器或编织器而不是 如果您的需求超出了 Spring AOP 提供的设施,则可以添加到 Spring AOP 中 独自。
Spring 附带了一个小型 AspectJ aspect 库,该库可在
分发为spring-aspects.jar
.您需要按顺序将其添加到类路径中
使用其中的方面。使用 AspectJ 依赖注入具有 Spring 的域对象和其他 Spring 方面 对于 AspectJ 讨论
该库的内容以及如何使用它。使用 Spring IoC 配置 AspectJ 方面讨论了如何
依赖注入使用 AspectJ 编译器编织的 AspectJ 方面。最后,Spring 框架中使用 AspectJ 的加载时编织介绍了 Spring 应用程序的加载时编织
使用 AspectJ。
使用 AspectJ 与 Spring 进行依赖注入域对象
Spring 容器实例化并配置应用程序中定义的 bean
上下文。也可以要求 bean 工厂配置预先存在的
对象,给定包含要应用的配置的 Bean 定义的名称。spring-aspects.jar
包含一个利用
允许注入任何对象的依赖项的能力。该支持旨在
用于在任何容器控制之外创建的对象。域对象
通常属于这一类,因为它们通常是使用new
运算符或 ORM 工具作为数据库查询的结果。
这@Configurable
注释将类标记为符合 Spring 驱动的条件
配置。在最简单的情况下,您可以纯粹将其用作标记注释,如
以下示例显示:
-
Java
-
Kotlin
package com.xyz.domain;
import org.springframework.beans.factory.annotation.Configurable;
@Configurable
public class Account {
// ...
}
package com.xyz.domain
import org.springframework.beans.factory.annotation.Configurable
@Configurable
class Account {
// ...
}
当以这种方式用作标记接口时,Spring 会配置
带注释的类型 (Account
,在本例中),使用 bean 定义(通常
prototype-scoped),与完全限定类型名称同名
(com.xyz.domain.Account
).由于 bean 的默认名称是
完全限定的名称,声明原型定义的便捷方式
是省略id
属性,如以下示例所示:
<bean class="com.xyz.domain.Account" scope="prototype">
<property name="fundsTransferService" ref="fundsTransferService"/>
</bean>
如果要显式指定要使用的原型 Bean 定义的名称,请 可以直接在注释中执行此作,如以下示例所示:
-
Java
-
Kotlin
package com.xyz.domain;
import org.springframework.beans.factory.annotation.Configurable;
@Configurable("account")
public class Account {
// ...
}
package com.xyz.domain
import org.springframework.beans.factory.annotation.Configurable
@Configurable("account")
class Account {
// ...
}
Spring 现在查找名为account
并将其用作
配置 new 的定义Account
实例。
您还可以使用自动装配来避免在
都。要让 Spring 应用自动布线,请使用autowire
属性的@Configurable
注解。您可以指定@Configurable(autowire=Autowire.BY_TYPE)
或@Configurable(autowire=Autowire.BY_NAME)
用于按类型或名称自动接线,
分别。作为替代方案,最好指定显式的、注释驱动的
依赖注入@Configurable
豆子通过@Autowired
或@Inject
在字段或方法级别(有关更多详细信息,请参阅基于注释的容器配置)。
最后,您可以为新中的对象引用启用 Spring 依赖项检查
使用dependencyCheck
属性(例如,@Configurable(autowire=Autowire.BY_NAME,dependencyCheck=true)
).如果此属性为
设置为true
,Spring 在配置后验证所有属性(其中
不是原语或集合)已被设置。
请注意,单独使用注释不会执行任何作。它是AnnotationBeanConfigurerAspect
在spring-aspects.jar
作用于
注释。从本质上讲,该方面说,“从初始化返回后
一个类型的新对象,用@Configurable
,配置新创建的对象
根据注释的属性使用 Spring“。在这种情况下,
“初始化”是指新实例化的对象(例如,实例化的对象
使用new
运算符)以及Serializable
正在进行的对象
反序列化(例如,通过 readResolve())。
上一段的关键短语之一是“本质上”。在大多数情况下,
“从新对象的初始化中返回后”的确切语义是
好。在这种情况下,“初始化后”意味着依赖项是
在构建对象后注入。这意味着依赖项
不可用于类的构造函数体。如果你想要
依赖项在构造函数体运行之前注入,因此
可用于构造函数的主体,您需要在
您可以找到有关各种切入点的语言语义的更多信息 类型 在 AspectJ 编程指南的本附录中。 |
为此,必须使用 AspectJ 编织器编织带注释的类型。您可以
使用构建时的 Ant 或 Maven 任务来执行此作(例如,请参阅 AspectJ 开发
环境指南)或加载时编织(请参阅 Spring 框架中使用 AspectJ 进行加载时编织)。这AnnotationBeanConfigurerAspect
本身需要由 Spring 配置(以便获得
对用于配置新对象的 Bean 工厂的引用)。如果你
使用基于 Java 的配置,可以添加@EnableSpringConfigured
到任何@Configuration
类,如下所示:
-
Java
-
Kotlin
@Configuration
@EnableSpringConfigured
public class AppConfig {
}
@Configuration
@EnableSpringConfigured
class AppConfig {
}
如果您更喜欢基于 XML 的配置,则 Springcontext
Namespace定义了一个方便的context:spring-configured
元素,您可以按如下方式使用它:
<context:spring-configured/>
实例@Configurable
在配置方面之前创建的对象
导致向调试日志发出消息,并且没有配置
发生的对象。一个示例可能是 Spring 配置中的 bean,它创建了
domain 对象。在这种情况下,您可以使用depends-on
bean 属性来手动指定 bean 依赖于
配置方面。以下示例演示如何使用depends-on
属性:
<bean id="myService"
class="com.xyz.service.MyService"
depends-on="org.springframework.beans.factory.aspectj.AnnotationBeanConfigurerAspect">
<!-- ... -->
</bean>
不激活@Configurable 通过 Bean Configurer 方面进行处理,除非您
实际上意味着在运行时依赖其语义。特别是,请确保您这样做
不使用@Configurable 在注册为常规 Spring Bean 的 Bean 类上
与容器。这样做会导致双重初始化,一次通过
容器和一次通过方面。 |
单元测试@Configurable
对象
的目标之一@Configurable
支持是启用独立的单元测试
域对象的属性,而没有与硬编码查找相关的困难。
如果@Configurable
类型没有被 AspectJ 编织,注释没有影响
在单元测试期间。您可以在对象中设置模拟或存根属性引用
测试并照常进行。如果@Configurable
类型已被 AspectJ 编织,
您仍然可以像往常一样在容器外部进行单元测试,但您会看到一条警告
每次构造@Configurable
对象,表示它有
未由 Spring 配置。
使用多个应用程序上下文
这AnnotationBeanConfigurerAspect
用于实现@Configurable
支持
是 AspectJ 单例方面。单例方面的作用域与作用域相同
之static
members:每个方面实例有一个ClassLoader
定义类型。
这意味着,如果您在同一个ClassLoader
层次结构,您需要考虑在哪里定义@EnableSpringConfigured
bean 和
放置位置spring-aspects.jar
在类路径上。
考虑一个典型的 Spring Web 应用程序配置,它有一个共享的父应用程序
定义通用业务服务的上下文,支持这些服务所需的一切,
以及每个 Servlet 的一个子应用程序上下文(其中包含特定的定义
到该 servlet)。所有这些上下文都共存于同一个环境中ClassLoader
等级制度
因此,AnnotationBeanConfigurerAspect
只能保存对其中一个的引用。
在这种情况下,我们建议定义@EnableSpringConfigured
共享中的 bean
(父)应用程序上下文。这定义了您可能想要的服务
注入到域对象中。结果是无法配置域对象
使用
@Configurable机制(无论如何,这可能不是你想做的事情)。
在同一容器中部署多个 Web 应用程序时,请确保每个
Web 应用程序加载spring-aspects.jar
通过使用自己的ClassLoader
(例如,通过将spring-aspects.jar
在WEB-INF/lib
).如果spring-aspects.jar
仅添加到容器范围的类路径(因此由共享父级ClassLoader
),所有 Web 应用程序共享相同的 aspect 实例(这可能是
不是你想要的)。
AspectJ 的其他 Spring 方面
除了@Configurable
方面spring-aspects.jar
包含一个 AspectJ
可用于驱动 Spring 对类型和方法的事务管理的方面
用@Transactional
注解。这主要适用于以下用户:
想要在 Spring 容器之外使用 Spring Framework 的事务支持。
解释的方面@Transactional
注释是AnnotationTransactionAspect
.使用此方面时,必须对
实现类(或该类中的方法或两者兼而有之),而不是接口(如果
any) 的类实现。AspectJ 遵循 Java 的规则,即对
接口不会继承。
一个@Transactional
类上的注释指定
类中任何公共作的执行。
一个@Transactional
类中方法的注释将覆盖默认值
类注释(如果存在)给出的交易语义。任何方法
可见性可以进行注释,包括私有方法。注释非公共方法
直接是获取执行此类方法的事务分界的唯一方法。
从 Spring Framework 4.2 开始,spring-aspects 提供了一个类似的方面,提供
与标准完全相同的功能jakarta.transaction.Transactional 注解。检查JtaAnnotationTransactionAspect 了解更多详情。 |
对于想要使用 Spring 配置和事务的 AspectJ 程序员
管理支持,但不想(或不能)使用注释,spring-aspects.jar
还包含abstract
您可以扩展以提供自己的切入点的方面
定义。请参阅AbstractBeanConfigurerAspect
和AbstractTransactionAspect
方面以获取更多信息。例如,以下内容
摘录显示了如何编写一个方面来配置对象的所有实例
使用与
完全限定的类名:
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(..)) &&
CommonPointcuts.inDomainModel() &&
this(beanInstance);
}
使用 Spring IoC 配置 AspectJ 方面
当您将 AspectJ 方面与 Spring 应用程序一起使用时,很自然地想要和
期望能够使用 Spring 配置这些方面。AspectJ 运行时本身是
负责 aspect 创建,以及配置 AspectJ-created 的手段
方面通过 Spring 取决于 AspectJ 实例化模型(per-xxx
条款)
由方面使用。
大多数 AspectJ 方面都是单例方面。这些的配置
方面很容易。您可以创建一个 bean 定义,该定义引用 aspect 类型为
normal 并包含factory-method="aspectOf"
bean 属性。这确保了
Spring 通过向 AspectJ 请求它来获取 aspect 实例,而不是尝试创建
实例本身。以下示例演示如何使用factory-method="aspectOf"
属性:
<bean id="profiler" class="com.xyz.profiler.Profiler"
factory-method="aspectOf"> (1)
<property name="profilingStrategy" ref="jamonProfilingStrategy"/>
</bean>
1 | 请注意factory-method="aspectOf" 属性 |
非单例方面更难配置。但是,可以通过创建原型 Bean 定义并使用@Configurable
支持spring-aspects.jar
在 Aspect 实例创建 bean 后配置它们AspectJ 运行时。
如果你有一些@AspectJ方面要用 AspectJ 编织(例如,对领域模型类型使用加载时编织)和其他你想要的@AspectJ方面与 Spring AOP 一起使用,并且这些方面都在 Spring 中配置,你需要告诉 Spring AOP @AspectJ自动代理支持的哪个确切子集配置中定义的@AspectJ方面应该用于自动代理。 您可以 为此,请使用一个或多个<include/>
元素<aop:aspectj-autoproxy/>
声明。每<include/>
元素指定一个名称模式,并且只有带有
与至少一种模式匹配的名称用于 Spring AOP 自动代理
配置。以下示例演示如何使用<include/>
元素:
<aop:aspectj-autoproxy>
<aop:include name="thisBean"/>
<aop:include name="thatBean"/>
</aop:aspectj-autoproxy>
不要被名称误导<aop:aspectj-autoproxy/> 元素。使用它
导致创建 Spring AOP 代理。@AspectJ样式的方面
这里使用声明,但不涉及 AspectJ 运行时。 |
在 Spring 框架中使用 AspectJ 进行加载时编织
加载时间编织 (LTW) 是指将 AspectJ 方面编织成 应用程序的类文件,因为它们被加载到 Java 虚拟机 (JVM) 中。 本节的重点是在 Spring 框架。本节不是对 LTW 的一般介绍。有关 LTW 的细节和仅使用 AspectJ 配置 LTW(Spring 不是 完全涉及),请参阅 AspectJ 的 LTW 部分 开发环境指南。
Spring Framework 为 AspectJ LTW 带来的价值在于实现了许多
对织造过程进行更细粒度的控制。“Vanilla”AspectJ LTW 是通过使用
Java (5+) 代理,通过在启动
JVM.因此,它是一个 JVM 范围的设置,在某些情况下可能没问题,但通常是
有点太粗糙了。启用 Spring 的 LTW 允许您在
每-ClassLoader
基础,哪个粒度更细,哪个可以做更多
在“单 JVM-多应用程序”环境中的意义(例如在典型的
应用服务器环境)。
此外,在某些环境中,此支持使
加载时编织,而无需对应用程序服务器的启动进行任何修改
需要添加的脚本-javaagent:path/to/aspectjweaver.jar
或(正如我们所描述的
在本节后面)-javaagent:path/to/spring-instrument.jar
.开发人员配置
应用程序上下文,以启用加载时编织,而不是依赖管理员
通常负责部署配置(例如启动脚本)的人。
现在推销已经结束,让我们首先浏览一个 AspectJ 的快速示例 LTW 的 Spring,然后是 例。有关完整的示例,请参阅基于 Spring Framework 的 Petclinic 示例应用程序。
第一个例子
假设您是一名应用程序开发人员,其任务是诊断系统中某些性能问题的原因。而不是打破一个分析工具,我们将打开一个简单的分析方面,让我们快速获得一些性能指标。然后我们可以应用更细粒度的分析工具立即应用于该特定区域。
此处提供的示例使用 XML 配置。您还可以配置和将@AspectJ与 Java 配置一起使用。具体来说,您可以使用@EnableLoadTimeWeaving 注释作为替代<context:load-time-weaver/> (详情见下文)。 |
以下示例显示了分析方面,它并不花哨。它是一个基于时间的分析器,使用方面声明的@AspectJ样式:
-
Java
-
Kotlin
package com.xyz;
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 * com.xyz..*.*(..))")
public void methodsToBeProfiled(){}
}
package com.xyz
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
class ProfilingAspect {
@Around("methodsToBeProfiled()")
fun profile(pjp: ProceedingJoinPoint): Any? {
val sw = StopWatch(javaClass.simpleName)
try {
sw.start(pjp.getSignature().getName())
return pjp.proceed()
} finally {
sw.stop()
println(sw.prettyPrint())
}
}
@Pointcut("execution(public * com.xyz..*.*(..))")
fun methodsToBeProfiled() {
}
}
我们还需要创建一个META-INF/aop.xml
文件,以通知 AspectJ 编织者
我们想编织我们的ProfilingAspect
进入我们的班级。这个文件约定,即
Java 类路径上存在一个(或多个文件),称为META-INF/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 and sub-packages -->
<include within="com.xyz..*"/>
</weaver>
<aspects>
<!-- weave in just this aspect -->
<aspect name="com.xyz.ProfilingAspect"/>
</aspects>
</aspectj>
建议仅编织特定类(通常是
应用程序包,如aop.xml 上面的例子)按顺序排列
以避免 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
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="com.xyz.StubEntitlementCalculationService"/>
<!-- this switches on the load-time weaving -->
<context:load-time-weaver/>
</beans>
现在,所有必需的工件(方面、META-INF/aop.xml
文件,并且 Spring 配置)就位,我们可以创建以下内容
驱动程序类,并带有main(..)
演示 LTW 实际效果的方法:
-
Java
-
Kotlin
package com.xyz;
// imports
public class Main {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
EntitlementCalculationService service =
ctx.getBean(EntitlementCalculationService.class);
// the profiling aspect is 'woven' around this method execution
service.calculateEntitlement();
}
}
package com.xyz
// imports
fun main() {
val ctx = ClassPathXmlApplicationContext("beans.xml")
val service = ctx.getBean(EntitlementCalculationService.class)
// the profiling aspect is 'woven' around this method execution
service.calculateEntitlement()
}
我们还有最后一件事要做。本节的引言确实说过,可以
在每个ClassLoader
与 Spring 的基础,这是真的。
但是,对于此示例,我们使用Java代理(Spring随附)来打开LTW。
我们使用以下命令运行Main
前面显示的类:
java -javaagent:C:/projects/xyz/lib/spring-instrument.jar com.xyz.Main
这-javaagent
是用于指定和启用代理的标志
检测在 JVM 上运行的程序。Spring Framework 附带了这样的
agent,则InstrumentationSavingAgent
,它打包在spring-instrument.jar
作为-javaagent
参数
前面的示例。
执行Main
程序看起来像下一个例子。
(我已经介绍了一个Thread.sleep(..)
语句添加到calculateEntitlement()
实现,以便分析器实际捕获 0 以外的内容
毫秒(01234
毫秒不是 AOP 引入的开销)。
以下列表显示了运行分析器时获得的输出:
Calculating entitlement StopWatch 'ProfilingAspect': running time (millis) = 1234 ------ ----- ---------------------------- ms % Task name ------ ----- ---------------------------- 01234 100% calculateEntitlement
由于此 LTW 是通过使用成熟的 AspectJ 来实现的,因此我们不仅限于提供建议
春豆。以下细微变化Main
程序产生相同的结果
结果:
-
Java
-
Kotlin
package com.xyz;
// imports
public class Main {
public static void main(String[] args) {
new ClassPathXmlApplicationContext("beans.xml");
EntitlementCalculationService service =
new StubEntitlementCalculationService();
// the profiling aspect will be 'woven' around this method execution
service.calculateEntitlement();
}
}
package com.xyz
// imports
fun main(args: Array<String>) {
ClassPathXmlApplicationContext("beans.xml")
val service = StubEntitlementCalculationService()
// the profiling aspect will be 'woven' around this method execution
service.calculateEntitlement()
}
请注意,在前面的程序中,我们如何引导 Spring 容器和
然后创建一个新的实例StubEntitlementCalculationService
完全在外面
Spring的背景。分析建议仍然被编织在其中。
诚然,这个例子很简单。但是,Spring 中 LTW 支持的基础知识 在前面的示例中都介绍过,本节的其余部分将解释 详细说明每个配置和用法背后的“原因”。
这ProfilingAspect 这个例子中使用的可能是基本的,但它非常有用。这是一个
开发人员可以在开发过程中使用的开发时方面的一个很好的例子
然后轻松地从正在部署的应用程序的构建中排除
进入 UAT 或生产。 |
方面
您在 LTW 中使用的方面必须是 AspectJ 方面。你可以把它们写进去 要么是 AspectJ 语言本身,要么你可以用 @AspectJ 风格编写你的方面。 然后,您的切面都是有效的 AspectJ 和 Spring AOP 切面。 此外,编译后的方面类需要在类路径上可用。
META-INF/aop.xml
AspectJ LTW 基础结构使用一个或多个META-INF/aop.xml
位于 Java 类路径上的文件(直接或更典型的是 jar 文件中)。
例如:
<!DOCTYPE aspectj PUBLIC "-//AspectJ//DTD//EN" "https://www.eclipse.org/aspectj/dtd/aspectj.dtd">
<aspectj>
<weaver>
<!-- only weave classes in our application-specific packages and sub-packages -->
<include within="com.xyz..*"/>
</weaver>
</aspectj>
建议仅编织特定类(通常是
应用程序包,如aop.xml 上面的例子)按顺序排列
以避免 AspectJ 转储文件和警告等副作用。
从效率的角度来看,这也是一个最佳实践。 |
该文件的结构和内容详见 AspectJ 参考的 LTW 部分
文档。因为aop.xml
文件是 100% AspectJ,我们在这里不再进一步描述它。
所需库 (JARS)
至少,您需要以下库才能使用 Spring Framework 的支持 对于 AspectJ LTW:
-
spring-aop.jar
-
aspectjweaver.jar
如果您使用 Spring 提供的代理来启用检测,您还需要:
-
spring-instrument.jar
弹簧配置
Spring LTW 支持的关键组件是LoadTimeWeaver
接口(在org.springframework.instrument.classloading
package)和众多实现
其中与春季发行版一起发货。一个LoadTimeWeaver
负责
添加一个或多个java.lang.instrument.ClassFileTransformers
设置为ClassLoader
在
运行时,它为各种有趣的应用程序打开了大门,其中之一
恰好是方面的 LTW。
如果您不熟悉运行时类文件转换的概念,请参阅
javadoc API 文档java.lang.instrument 打包后再继续。
虽然该文档并不全面,但至少您可以看到关键接口
和类(供您阅读本节时参考)。 |
配置LoadTimeWeaver
对于特定的ApplicationContext
可以像
添加一行。(请注意,您几乎肯定需要使用ApplicationContext
作为 Spring 容器 — 通常,一个BeanFactory
莫
足够了,因为 LTW 支持BeanFactoryPostProcessors
.)
要启用 Spring Framework 的 LTW 支持,您需要配置一个LoadTimeWeaver
,
这通常是通过使用@EnableLoadTimeWeaving
注释,如下所示:
-
Java
-
Kotlin
@Configuration
@EnableLoadTimeWeaving
public class AppConfig {
}
@Configuration
@EnableLoadTimeWeaving
class AppConfig {
}
或者,如果您更喜欢基于 XML 的配置,请使用<context:load-time-weaver/>
元素。请注意,该元素在context
Namespace。以下示例演示如何使用<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 实现 |
---|---|
在 Apache Tomcat 中运行 |
|
在 GlassFish 中运行(仅限于 EAR 部署) |
|
|
|
JVM 从 Spring 开始 |
|
回退,期望底层 ClassLoader 遵循通用约定
(即 |
|
请注意,该表仅列出了LoadTimeWeavers
当您
使用DefaultContextLoadTimeWeaver
.您可以准确指定哪个LoadTimeWeaver
实现以使用。
要指定特定的LoadTimeWeaver
使用 Java 配置时,实现LoadTimeWeavingConfigurer
接口并覆盖getLoadTimeWeaver()
方法。
以下示例指定ReflectiveLoadTimeWeaver
:
-
Java
-
Kotlin
@Configuration
@EnableLoadTimeWeaving
public class AppConfig implements LoadTimeWeavingConfigurer {
@Override
public LoadTimeWeaver getLoadTimeWeaver() {
return new ReflectiveLoadTimeWeaver();
}
}
@Configuration
@EnableLoadTimeWeaving
class AppConfig : LoadTimeWeavingConfigurer {
override fun getLoadTimeWeaver(): LoadTimeWeaver {
return ReflectiveLoadTimeWeaver()
}
}
如果使用基于 XML 的配置,则可以指定完全限定的类名
作为weaver-class
属性<context:load-time-weaver/>
元素。同样,以下示例指定了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
由配置定义和注册的,可以稍后
使用众所周知的名称从 Spring 容器中检索,loadTimeWeaver
.
请记住,LoadTimeWeaver
仅作为 Spring LTW 的机制存在
基础结构,以添加一个或多个ClassFileTransformers
.实际的ClassFileTransformer
LTW 是ClassPreProcessorAgentAdapter
(从
这org.aspectj.weaver.loadtime
package) 类。请参阅ClassPreProcessorAgentAdapter
class 了解更多详细信息,因为具体如何
实际进行的编织超出了本文档的范围。
还有一个配置的最后一个属性需要讨论:aspectjWeaving
属性(或aspectj-weaving
如果您使用 XML)。此属性控制 LTW
是否启用。它接受三个可能的值之一,默认值为autodetect
如果该属性不存在。下表总结了这三个
可能的值:
注释值 | XML 值 | 解释 |
---|---|---|
|
|
AspectJ 编织已打开,并且根据需要在加载时编织 Aspect 。 |
|
|
LTW 已关闭。加载时没有任何方面是编织的。 |
|
|
如果 Spring LTW 基础设施可以找到至少一个 |
特定于环境的配置
最后一部分包含您需要的任何其他设置和配置 当您在应用程序服务器和 Web 等环境中使用 Spring 的 LTW 支持时 器皿。
Tomcat、JBoss、WildFly
Tomcat 和 JBoss/WildFly 提供了一个通用的应用程序ClassLoader
能够本地的
仪表。Spring 的原生 LTW 可能会利用这些 ClassLoader 实现
提供 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 选项:
-javaagent:/path/to/spring-instrument.jar
请注意,这需要修改 JVM 启动脚本,这可能会阻止您 在应用程序服务器环境中使用它(取决于您的服务器和您的 运营策略)。也就是说,对于每个 JVM 一个应用程序部署,例如独立 Spring Boot 应用程序,您通常在任何情况下都可以控制整个 JVM 设置。