此版本仍在开发中,尚未被视为稳定版本。最新的稳定版本请使用 Spring Framework 6.1.13! |
此版本仍在开发中,尚未被视为稳定版本。最新的稳定版本请使用 Spring Framework 6.1.13! |
到目前为止,我们在本章中介绍的所有内容都是纯粹的 Spring AOP。在本节中, 我们看看如何使用 AspectJ 编译器或 weaver 来代替 OR IN 如果您的需求超出了 Spring AOP 提供的功能,则可添加到 Spring AOP 中 独自。
Spring 附带了一个小型的 AspectJ 方面库,该库可在
分发为 .您需要按顺序将其添加到 Classpath 中
以使用其中的 aspects。使用 AspectJ 对 Spring 和 Spring 的其他 Spring 方面进行依赖注入 Domain Objects 和 AspectJ 讨论
此库的内容以及如何使用它。使用 Spring IoC 配置 AspectJ 方面 讨论了如何
dependency 注入使用 AspectJ 编译器编织的 AspectJ 切面。最后,Spring 框架中使用 AspectJ 的加载时编织 提供了对 Spring 应用程序的加载时编织的介绍
使用 AspectJ。spring-aspects.jar
使用 AspectJ 与 Spring 进行依赖注入域对象
Spring 容器实例化并配置应用程序中定义的 bean
上下文。也可以要求 bean factory 配置预先存在的
object,给定包含要应用的配置的 bean 定义的名称。 包含一个 annotation-driven 切面,该 aspect 利用此
允许对任何对象进行依赖关系注入的能力。该支持旨在
用于在任何容器的控制之外创建的对象。域对象
通常属于这一类,因为它们通常是使用 Operator 以编程方式创建的,或者作为数据库查询的结果由 ORM 工具创建。spring-aspects.jar
new
该 Comments 将类标记为符合 Spring 驱动的条件
配置。在最简单的情况下,您可以将其单独用作标记注释,因为
以下示例显示:@Configurable
-
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 会配置
注解类型(在本例中为)使用 Bean 定义(通常为
prototype-scoped),其名称与完全限定类型名称相同
().由于通过 XML 定义的 Bean 的默认名称是
其类型的完全限定名称,这是声明原型定义的便捷方式
是省略该属性,如下例所示:Account
com.xyz.domain.Account
id
<bean class="com.xyz.domain.Account" scope="prototype">
<property name="fundsTransferService" ref="fundsTransferService"/>
</bean>
如果要显式指定要使用的原型 bean 定义的名称,则 可以直接在 Comments 中执行此操作,如下例所示:
-
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 现在查找名为 的 bean 定义,并将其用作
定义来配置新实例。account
Account
您还可以使用自动装配来避免在
都。要让 Spring 应用自动装配,请使用 Comments 的属性。您可以按类型或名称为自动装配指定 或 。
分别。作为替代方案,最好指定显式的、注解驱动的
通过字段或方法级别或在字段或方法级别对 bean 进行依赖注入(有关更多详细信息,请参阅基于 Comments 的容器配置)。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
请注意,单独使用 Comments 不会执行任何操作。正是 in 作用于
注释。从本质上讲,该 aspect 表示,“从
带有 注释 的新对象,配置新创建的对象
根据注释的属性使用 Spring”。在此上下文中,
“initialization” 是指新实例化的对象(例如,实例化的对象
使用运算符)以及正在发生的对象
反序列化(例如,通过 readResolve())。AnnotationBeanConfigurerAspect
spring-aspects.jar
@Configurable
new
Serializable
上一段中的关键词之一是“本质上”。在大多数情况下,
“After returning from the initialization of a new object” 的确切语义是
好。在这种情况下,“初始化后”意味着依赖项是
在构造对象后注入。这意味着依赖项
不能在类的构造函数主体中使用。如果您希望
依赖项,以便在构造函数主体运行之前注入,因此
可用于构造函数的主体,您需要在声明中定义它,如下所示:
您可以找到有关各种切入点的语言语义的更多信息 在 AspectJ 编程指南的这个附录中键入 AspectJ。 |
为此,必须用 AspectJ weaver 编织带注释的类型。您可以
使用构建时 Ant 或 Maven 任务来执行此操作(例如,参见 AspectJ 开发
Environment Guide)或加载时编织(参见 Spring 框架中使用 AspectJ 的加载时编织)。本身需要由 Spring 配置(为了获得
对用于配置新对象的 Bean Factory 的引用)。您可以定义
相关配置如下:AnnotationBeanConfigurerAspect
-
Java
-
Kotlin
-
Xml
@Configuration
@EnableSpringConfigured
public class ApplicationConfiguration {
}
@Configuration
@EnableSpringConfigured
class ApplicationConfiguration
<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:spring-configured />
</beans>
在配置 aspect 之前创建的对象的实例
导致向调试日志发出一条消息,并且没有配置
对象正在发生。一个例子可能是 Spring 配置中的一个 bean,它创建
domain 对象。在这种情况下,您可以使用 bean 属性手动指定 bean 依赖于
configuration 方面。以下示例演示如何使用该属性:@Configurable
depends-on
depends-on
<bean id="myService"
class="com.xyz.service.MyService"
depends-on="org.springframework.beans.factory.aspectj.AnnotationBeanConfigurerAspect">
<!-- ... -->
</bean>
不要通过 bean 配置器方面激活处理,除非你
真的意味着在运行时依赖它的语义。特别是,请确保您这样做
不用于注册为常规 Spring bean 的 bean 类
与容器一起。这样做会导致双重初始化,一次通过
容器和一次通过方面。@Configurable @Configurable |
单元测试对象@Configurable
支持的目标之一是启用独立的单元测试
的域对象,而没有与硬编码查找相关的困难。
如果类型尚未被 AspectJ 编织,则注解没有影响
在单元测试期间。您可以在
测试并照常进行。如果类型已经被 AspectJ 编织,则
您仍然可以像往常一样在容器外部进行单元测试,但您会看到一条警告
message 中,每次构造一个对象时,都会显示该对象具有
未由 Spring 配置。@Configurable
@Configurable
@Configurable
@Configurable
使用多个应用程序上下文
用于实施支持的
是 AspectJ 单例 aspect。单例 aspect 的 scope 与 scope 相同
的成员:有一个定义类型的 aspect 实例。
这意味着,如果在同一层次结构中定义多个应用程序上下文,则需要考虑在何处定义 Bean 和
在 Classpath 上放置的位置。AnnotationBeanConfigurerAspect
@Configurable
static
ClassLoader
ClassLoader
@EnableSpringConfigured
spring-aspects.jar
考虑一个典型的 Spring Web 应用程序配置,该配置具有共享的父应用程序
定义常见业务服务的上下文,支持这些服务所需的一切,
以及每个 servlet 的一个子应用程序上下文(其中包含特定于
添加到该 Servlet 中)。所有这些上下文都共存于同一层次结构中,
因此 可以只保存对其中一个的引用。
在这种情况下,我们建议在共享的
(父)应用程序上下文。这定义了你可能想要的服务
inject 到 domain objects 中。结果是您无法配置域对象
引用子(特定于 servlet)上下文中定义的 bean,方法是使用
@Configurable机制(这可能不是你想做的事情)。ClassLoader
AnnotationBeanConfigurerAspect
@EnableSpringConfigured
在同一容器中部署多个 Web 应用程序时,请确保每个
Web 应用程序使用自己的类型(例如,通过将 放置在 )来加载类型。如果仅添加到容器范围的 Classpath 中(因此由共享父级加载),则所有 Web 应用程序共享相同的 aspect 实例(这可能是
不是你想要的)。spring-aspects.jar
ClassLoader
spring-aspects.jar
WEB-INF/lib
spring-aspects.jar
ClassLoader
上一段中的关键词之一是“本质上”。在大多数情况下,
“After returning from the initialization of a new object” 的确切语义是
好。在这种情况下,“初始化后”意味着依赖项是
在构造对象后注入。这意味着依赖项
不能在类的构造函数主体中使用。如果您希望
依赖项,以便在构造函数主体运行之前注入,因此
可用于构造函数的主体,您需要在声明中定义它,如下所示:
您可以找到有关各种切入点的语言语义的更多信息 在 AspectJ 编程指南的这个附录中键入 AspectJ。 |
不要通过 bean 配置器方面激活处理,除非你
真的意味着在运行时依赖它的语义。特别是,请确保您这样做
不用于注册为常规 Spring bean 的 bean 类
与容器一起。这样做会导致双重初始化,一次通过
容器和一次通过方面。@Configurable @Configurable |
AspectJ 的其他 Spring 方面
除了 aspect 之外,还包含一个 AspectJ
方面,您可以使用它来驱动 Spring 的类型和方法的事务管理
使用注释进行注释。这主要适用于满足以下条件的用户
希望在 Spring 容器之外使用 Spring Framework 的事务支持。@Configurable
spring-aspects.jar
@Transactional
解释注释的方面是 .当您使用此 aspect 时,您必须注释
implementation 类(或该类中的方法,或两者),而不是接口(如果
any) 实现。AspectJ 遵循 Java 的规则,即
接口不是继承的。@Transactional
AnnotationTransactionAspect
类上的注释指定了
类中任何公共操作的执行。@Transactional
类中方法的注释将覆盖默认值
类注解(如果存在)给出的交易语义。任何
visibility 可以被注释,包括 private methods。对非公共方法进行注解
directly 是获取执行此类方法的事务划分的唯一方法。@Transactional
从 Spring Framework 4.2 开始,提供了一个类似的方面,它提供了
与 Standard 注释的功能完全相同。查看更多详情。spring-aspects jakarta.transaction.Transactional JtaAnnotationTransactionAspect |
对于想要使用 Spring 配置和事务的 AspectJ 程序员
管理支持,但不想(或不能)使用注解,也包含您可以扩展以提供自己的切入点的方面
定义。有关更多信息,请参阅 和 方面的来源。例如,以下
Excerpt 展示了如何编写一个 aspect 来配置对象的所有实例
使用与
完全限定的类名: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 Framework 4.2 开始,提供了一个类似的方面,它提供了
与 Standard 注释的功能完全相同。查看更多详情。spring-aspects jakarta.transaction.Transactional JtaAnnotationTransactionAspect |
使用 Spring IoC 配置 AspectJ 方面
当你在 Spring 应用程序中使用 AspectJ 方面时,很自然地需要和
期望能够使用 Spring 配置这些方面。AspectJ 运行时本身是
负责 aspect 的创建,以及配置 AspectJ 创建的
通过 Spring 的 aspects 依赖于 AspectJ 实例化模型(子句)
由 aspect 使用。per-xxx
大多数 AspectJ 方面都是单例方面。这些的配置
方面很容易。您可以创建一个引用 aspect 类型的 bean 定义
normal 并包含 bean 属性。这可确保
Spring 通过向 AspectJ 请求 aspect 实例来获取它,而不是尝试创建
实例本身。以下示例演示如何使用该属性:factory-method="aspectOf"
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 定义并使用 support from 来配置 aspect 实例,一旦它们由
AspectJ 运行时。@Configurable
spring-aspects.jar
如果你有一些 @AspectJ 方面你想用 AspectJ 编织(例如,
对域模型类型使用加载时编织)和其他所需的@AspectJ方面
与 Spring AOP 一起使用,并且这些方面都在 Spring 中配置,那么
需要告诉 Spring AOP @AspectJ 自动代理支持是
配置中定义的@AspectJ方面都应用于自动代理。您可以
通过在声明中使用一个或多个元素来执行此操作。每个元素都指定一个名称模式,并且只有
与至少一个模式匹配的名称用于 Spring AOP 自动代理
配置。以下示例演示如何使用元素:<include/>
<aop:aspectj-autoproxy/>
<include/>
<include/>
<aop:aspectj-autoproxy>
<aop:include name="thisBean"/>
<aop:include name="thatBean"/>
</aop:aspectj-autoproxy>
不要被元素的名称误导。使用
导致创建 Spring AOP 代理。aspect 的 @AspectJ 样式
声明,但不涉及 AspectJ 运行时。<aop:aspectj-autoproxy/> |
1 | 请注意属性factory-method="aspectOf" |
不要被元素的名称误导。使用
导致创建 Spring AOP 代理。aspect 的 @AspectJ 样式
声明,但不涉及 AspectJ 运行时。<aop:aspectj-autoproxy/> |
在 Spring 框架中使用 AspectJ 进行加载时编织
加载时编织 (LTW) 是指将 AspectJ 方面编织成 应用程序的类文件,因为它们正在加载到 Java 虚拟机 (JVM) 中。 本节的重点是在 Spring 框架。本节不是 LTW 的一般介绍。有关完整详细信息 LTW 的细节和仅使用 AspectJ 配置 LTW(Spring 不是 involved 的 lw),参见 AspectJ 的 LTW 部分 开发环境指南。
Spring 框架为 AspectJ LTW 带来的价值在于支持很多
对编织过程进行更精细的控制。'Vanilla' AspectJ LTW 通过使用
Java (5+) 代理,在启动
JVM 的 JVM 中。因此,它是一个 JVM 范围的设置,在某些情况下可能很好,但通常是一个
有点太粗糙了。启用 Spring 的 LTW 允许您在
per- basis 的
在“single-JVM-multiple-application”环境中(例如在典型的
应用程序服务器环境)。ClassLoader
此外,在某些环境中,此支持支持
加载时编织,而无需对 Application Server 的启动进行任何修改
需要添加的脚本或 (正如我们所描述的
本节稍后部分)。开发人员配置
应用程序上下文,用于启用加载时编织,而不是依赖管理员
通常负责部署配置(如 Launch Script)的人员。-javaagent:path/to/aspectjweaver.jar
-javaagent:path/to/spring-instrument.jar
现在销售宣传已经结束,让我们首先看一个 AspectJ 的快速示例 LTW,后跟有关 例。有关完整示例,请参阅基于 Spring Framework 的 Petclinic 示例应用程序。
第一个例子
假设您是一名应用程序开发人员,其任务是诊断 系统中某些性能问题的原因。而不是分解 profiling 工具中,我们将打开一个简单的性能分析方面,它允许我们 快速获取一些性能指标。然后,我们可以应用更细粒度的分析 工具复制到该特定区域。
此处显示的示例使用 XML 配置。您还可以配置和
将 @AspectJ 与 Java 配置结合使用。具体来说,您可以使用 annotation 作为 Comments 的替代方法(有关详细信息,请参阅下文)。@EnableLoadTimeWeaving <context:load-time-weaver/> |
下面的示例显示了性能分析方面,这并不花哨。 它是一个基于时间的分析器,它使用 @AspectJ 样式的 aspect 声明:
-
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() {
}
}
我们还需要创建一个文件,以通知 AspectJ weaver
我们想将 OUR 编织到我们的 class 中。这个文件约定,即
Java 类路径上存在一个名为 is 的文件
标准 AspectJ.以下示例显示了该文件:META-INF/aop.xml
ProfilingAspect
META-INF/aop.xml
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>
建议只编织特定的类(通常是
application 包,如上例所示)按顺序
以避免副作用,例如 AspectJ 转储文件和警告。
从效率的角度来看,这也是一个最佳实践。aop.xml |
现在我们可以继续进行配置的 Spring 特定部分。我们需要
来配置 A(稍后解释)。此加载时 weaver 是
essential 组件,负责将 aspect 配置编织到一个 OR
more 文件添加到应用程序的类中。好处
问题是它不需要很多配置(还有更多
选项,但稍后会详细介绍),如
以下示例: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>
现在所有必需的工件(方面、文件和 Spring 配置)都已就位,我们可以创建以下内容
driver 类,其中包含一个演示 LTW 的方法:META-INF/aop.xml
main(..)
-
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()
}
我们还有最后一件事要做。本节的引言确实说过,人们可以
使用 Spring 有选择地打开 LTW,这是真的。
但是,在此示例中,我们使用 Java 代理(随 Spring 提供)来开启 LTW。
我们使用以下命令来运行前面显示的类:ClassLoader
Main
java -javaagent:C:/projects/xyz/lib/spring-instrument.jar com.xyz.Main
是用于指定和启用代理的标志
检测在 JVM 上运行的程序。Spring Framework 附带了这样一个
agent 的 ,它打包在 中,作为 中的参数值提供
前面的示例。-javaagent
InstrumentationSavingAgent
spring-instrument.jar
-javaagent
程序执行的输出类似于下一个示例。
(我在实现中引入了一个语句,以便分析器实际捕获 0 以外的内容
毫秒(毫秒不是 AOP 引入的开销)。
下面的清单显示了我们在运行 profiler 时得到的输出:Main
Thread.sleep(..)
calculateEntitlement()
01234
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 容器并
然后创建一个 Totally Outside 的新实例
Spring 的上下文。分析建议仍然被编织在一起。StubEntitlementCalculationService
诚然,这个例子很简单。但是,Spring 中 LTW 支持的基础知识 在前面的示例中都已介绍过,本节的其余部分将解释 每个配置和用法背后的 “为什么” 都很详细。
此示例中使用的 可能是基本的,但它非常有用。它是一个
开发人员可以在开发过程中使用的开发时方面的一个很好的示例
然后轻松地从正在部署的应用程序的构建中排除
进入 UAT 或生产。ProfilingAspect |
方面
你在 LTW 中使用的 aspect 必须是 AspectJ 方面。您可以将它们写入 要么是 AspectJ 语言本身,要么你可以用 @AspectJ 风格编写你的 Aspects。 因此,你的 aspect 都是有效的 AspectJ 和 Spring AOP 方面。 此外,编译后的 aspect 类需要在 Classpath 上可用。
META-INF/aop.xml
AspectJ LTW 基础结构是通过使用 Java 类路径上的一个或多个文件(直接或更常见的,在 jar 文件中)来配置的。
例如:META-INF/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>
</aspectj>
建议只编织特定的类(通常是
application 包,如上例所示)按顺序
以避免副作用,例如 AspectJ 转储文件和警告。
从效率的角度来看,这也是一个最佳实践。aop.xml |
此文件的结构和内容在 AspectJ 参考的 LTW 部分中有详细说明
文档。因为这个文件是 100% 的 AspectJ,所以我们在这里不再进一步描述它。aop.xml
必需的库 (JAR)
至少,您需要以下库才能使用 Spring Framework 的支持 对于 AspectJ LTW:
-
spring-aop.jar
-
aspectjweaver.jar
如果使用 Spring 提供的代理来启用 instrumentation ,则还需要:
-
spring-instrument.jar
Spring 配置
Spring 的 LTW 支持的关键组件是接口(在包中)和众多实现
的 Spring 发行版。A 负责
向 AT 添加一个或多个
runtime 的 runtime,它为各种有趣的应用程序打开了大门,其中之一就是
恰好是 aspects 的 LTW。LoadTimeWeaver
org.springframework.instrument.classloading
LoadTimeWeaver
java.lang.instrument.ClassFileTransformers
ClassLoader
如果您不熟悉运行时类文件转换的概念,请参阅
javadoc API 文档。
虽然该文档并不全面,但至少您可以看到关键界面
和 classes (供你阅读本节时参考)。java.lang.instrument |
为特定配置 可以像
添加一行。(请注意,您几乎肯定需要使用 an 作为 Spring 容器 — 通常,a 不是
足够了,因为 LTW 支持使用 .)LoadTimeWeaver
ApplicationContext
ApplicationContext
BeanFactory
BeanFactoryPostProcessors
要启用 Spring Framework 的 LTW 支持,您需要按如下方式配置 a:LoadTimeWeaver
-
Java
-
Kotlin
-
Xml
@Configuration
@EnableLoadTimeWeaving
public class ApplicationConfiguration {
}
@Configuration
@EnableLoadTimeWeaving
class ApplicationConfiguration
<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,例如 a 和 an 。
默认值是类,该类会尝试
以装饰自动检测到的 .“自动检测”的确切类型取决于您的运行时环境。
下表总结了各种实现:LoadTimeWeaver
AspectJWeavingEnabler
LoadTimeWeaver
DefaultContextLoadTimeWeaver
LoadTimeWeaver
LoadTimeWeaver
LoadTimeWeaver
运行时环境 | LoadTimeWeaver 实现 |
---|---|
在 Apache Tomcat 中运行 |
|
在 GlassFish 中运行(仅限于 EAR 部署) |
|
|
|
JVM 始于 Spring |
|
Fallback,期望底层 ClassLoader 遵循通用约定
(即和可选的方法) |
|
请注意,该表仅列出了在以下情况下自动检测到的
使用 .您可以准确指定要使用的实现。LoadTimeWeavers
DefaultContextLoadTimeWeaver
LoadTimeWeaver
要配置特定的 , 实现接口并覆盖方法
(或使用 XML 等效项)。
以下示例指定 :LoadTimeWeaver
LoadTimeWeavingConfigurer
getLoadTimeWeaver()
ReflectiveLoadTimeWeaver
-
Java
-
Kotlin
-
Xml
@Configuration
@EnableLoadTimeWeaving
public class CustomWeaverConfiguration implements LoadTimeWeavingConfigurer {
@Override
public LoadTimeWeaver getLoadTimeWeaver() {
return new ReflectiveLoadTimeWeaver();
}
}
@Configuration
@EnableLoadTimeWeaving
class CustomWeaverConfiguration : LoadTimeWeavingConfigurer {
override fun getLoadTimeWeaver(): LoadTimeWeaver {
return ReflectiveLoadTimeWeaver()
}
}
<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>
由配置定义和注册的 可以稍后
使用众所周知的名称从 Spring 容器中检索。
请记住,它仅作为 Spring 的 LTW 的机制存在
基础结构以添加一个或多个 .执行 LTW 的实际值是 (从
package) 类。有关更多详细信息,请参阅类的类级 javadoc,因为具体操作
编织实际发生超出了本文档的范围。LoadTimeWeaver
loadTimeWeaver
LoadTimeWeaver
ClassFileTransformers
ClassFileTransformer
ClassPreProcessorAgentAdapter
org.aspectj.weaver.loadtime
ClassPreProcessorAgentAdapter
还有最后一个配置属性需要讨论:属性(或者如果您使用 XML)。此属性控制 LTW
是否启用。它接受三个可能的值之一,默认值为属性不存在。下表总结了这三种
可能的值:aspectjWeaving
aspectj-weaving
autodetect
注释值 | XML 值 | 解释 |
---|---|---|
|
|
AspectJ 编织已打开,并且 aspect 会根据需要在 load 时进行编织。 |
|
|
LTW 已关闭。在加载时没有编织任何方面。 |
|
|
如果 Spring LTW 基础结构可以找到至少一个文件,
然后 AspectJ weaving 开启。否则,它处于关闭状态。这是默认值。 |
特定于环境的配置
最后一部分包含您需要的任何其他设置和配置 当您在应用程序服务器和 Web 等环境中使用 Spring 的 LTW 支持时 器皿。
Tomcat、JBoss、WildFly
Tomcat 和 JBoss/WildFly 提供了一个通用应用程序,它能够在本地
仪表。Spring 的原生 LTW 可以利用这些 ClassLoader 实现
提供 AspectJ 编织。
如前所述,您只需启用 load-time weaving。
具体来说,您无需修改 JVM 启动脚本即可添加 .ClassLoader
-javaagent:path/to/spring-instrument.jar
请注意,在 JBoss 上,你可能需要禁用应用服务器扫描以防止它
在应用程序实际启动之前加载类。快速解决方法是将
将一个名为以下内容的文件添加到项目中:WEB-INF/jboss-scanning.xml
<scanning xmlns="urn:jboss:scanning:1.0"/>
通用 Java 应用程序
当在 不支持的环境中需要类插桩时
具体实现,JVM 代理是通用的解决方案。
对于这种情况, Spring 提供了需要
特定于 Spring 的(但非常通用的)JVM 代理 ,自动检测
由 common 和 setups。LoadTimeWeaver
InstrumentationLoadTimeWeaver
spring-instrument.jar
@EnableLoadTimeWeaving
<context:load-time-weaver/>
要使用它,您必须通过提供 Spring 代理来启动虚拟机 以下 JVM 选项:
-javaagent:/path/to/spring-instrument.jar
请注意,这需要修改 JVM 启动脚本,这可能会阻止您 在应用程序服务器环境中使用它(取决于您的服务器和 操作策略)。也就是说,对于每个 JVM 一个应用程序的部署,例如独立的 Spring Boot 应用程序,您通常在任何情况下都可以控制整个 JVM 设置。
此处显示的示例使用 XML 配置。您还可以配置和
将 @AspectJ 与 Java 配置结合使用。具体来说,您可以使用 annotation 作为 Comments 的替代方法(有关详细信息,请参阅下文)。@EnableLoadTimeWeaving <context:load-time-weaver/> |
建议只编织特定的类(通常是
application 包,如上例所示)按顺序
以避免副作用,例如 AspectJ 转储文件和警告。
从效率的角度来看,这也是一个最佳实践。aop.xml |
此示例中使用的 可能是基本的,但它非常有用。它是一个
开发人员可以在开发过程中使用的开发时方面的一个很好的示例
然后轻松地从正在部署的应用程序的构建中排除
进入 UAT 或生产。ProfilingAspect |
建议只编织特定的类(通常是
application 包,如上例所示)按顺序
以避免副作用,例如 AspectJ 转储文件和警告。
从效率的角度来看,这也是一个最佳实践。aop.xml |
如果您不熟悉运行时类文件转换的概念,请参阅
javadoc API 文档。
虽然该文档并不全面,但至少您可以看到关键界面
和 classes (供你阅读本节时参考)。java.lang.instrument |
运行时环境 | LoadTimeWeaver 实现 |
---|---|
在 Apache Tomcat 中运行 |
|
在 GlassFish 中运行(仅限于 EAR 部署) |
|
|
|
JVM 始于 Spring |
|
Fallback,期望底层 ClassLoader 遵循通用约定
(即和可选的方法) |
|
注释值 | XML 值 | 解释 |
---|---|---|
|
|
AspectJ 编织已打开,并且 aspect 会根据需要在 load 时进行编织。 |
|
|
LTW 已关闭。在加载时没有编织任何方面。 |
|
|
如果 Spring LTW 基础结构可以找到至少一个文件,
然后 AspectJ weaving 开启。否则,它处于关闭状态。这是默认值。 |