对于最新的稳定版本,请使用 Spring Framework 6.2.10spring-doc.cadn.net.cn

将 AspectJ 与 Spring 应用程序一起使用

到目前为止,我们在本章中介绍的所有内容都是纯 Spring AOP。在本节中, 我们看看如何使用 AspectJ 编译器或编织器而不是 如果您的需求超出了 Spring AOP 提供的设施,则可以添加到 Spring AOP 中 独自。spring-doc.cadn.net.cn

Spring 附带了一个小型 AspectJ aspect 库,该库可在 分发为spring-aspects.jar.您需要按顺序将其添加到类路径中 使用其中的方面。使用 AspectJ 依赖注入具有 Spring 的域对象和其他 Spring 方面 对于 AspectJ 讨论 该库的内容以及如何使用它。使用 Spring IoC 配置 AspectJ 方面讨论了如何 依赖注入使用 AspectJ 编译器编织的 AspectJ 方面。最后,Spring 框架中使用 AspectJ 的加载时编织介绍了 Spring 应用程序的加载时编织 使用 AspectJ。spring-doc.cadn.net.cn

使用 AspectJ 与 Spring 进行依赖注入域对象

Spring 容器实例化并配置应用程序中定义的 bean 上下文。也可以要求 bean 工厂配置预先存在的 对象,给定包含要应用的配置的 Bean 定义的名称。spring-aspects.jar包含一个利用 允许注入任何对象的依赖项的能力。该支持旨在 用于在任何容器控制之外创建的对象。域对象 通常属于这一类,因为它们通常是使用new运算符或 ORM 工具作为数据库查询的结果。spring-doc.cadn.net.cn

@Configurable注释将类标记为符合 Spring 驱动的条件 配置。在最简单的情况下,您可以纯粹将其用作标记注释,如 以下示例显示:spring-doc.cadn.net.cn

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属性,如以下示例所示:spring-doc.cadn.net.cn

<bean class="com.xyz.domain.Account" scope="prototype">
	<property name="fundsTransferService" ref="fundsTransferService"/>
</bean>

如果要显式指定要使用的原型 Bean 定义的名称,请 可以直接在注释中执行此作,如以下示例所示:spring-doc.cadn.net.cn

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-doc.cadn.net.cn

您还可以使用自动装配来避免在 都。要让 Spring 应用自动布线,请使用autowire属性的@Configurable注解。您可以指定@Configurable(autowire=Autowire.BY_TYPE)@Configurable(autowire=Autowire.BY_NAME)用于按类型或名称自动接线, 分别。作为替代方案,最好指定显式的、注释驱动的 依赖注入@Configurable豆子通过@Autowired@Inject在字段或方法级别(有关更多详细信息,请参阅基于注释的容器配置)。spring-doc.cadn.net.cn

最后,您可以为新中的对象引用启用 Spring 依赖项检查 使用dependencyCheck属性(例如,@Configurable(autowire=Autowire.BY_NAME,dependencyCheck=true)).如果此属性为 设置为true,Spring 在配置后验证所有属性(其中 不是原语或集合)已被设置。spring-doc.cadn.net.cn

请注意,单独使用注释不会执行任何作。它是AnnotationBeanConfigurerAspectspring-aspects.jar作用于 注释。从本质上讲,该方面说,“从初始化返回后 一个类型的新对象,用@Configurable,配置新创建的对象 根据注释的属性使用 Spring“。在这种情况下, “初始化”是指新实例化的对象(例如,实例化的对象 使用new运算符)以及Serializable正在进行的对象 反序列化(例如,通过 readResolve())。spring-doc.cadn.net.cn

上一段的关键短语之一是“本质上”。在大多数情况下, “从新对象的初始化中返回后”的确切语义是 好。在这种情况下,“初始化后”意味着依赖项是 在构建对象后注入。这意味着依赖项 不可用于类的构造函数体。如果你想要 依赖项在构造函数体运行之前注入,因此 可用于构造函数的主体,您需要在@Configurable声明,如下所示:spring-doc.cadn.net.cn

@Configurable(preConstruction = true)
@Configurable(preConstruction = true)

您可以找到有关各种切入点的语言语义的更多信息 类型 在 AspectJ 编程指南本附录中spring-doc.cadn.net.cn

为此,必须使用 AspectJ 编织器编织带注释的类型。您可以 使用构建时的 Ant 或 Maven 任务来执行此作(例如,请参阅 AspectJ 开发 环境指南)或加载时编织(请参阅 Spring 框架中使用 AspectJ 进行加载时编织)。这AnnotationBeanConfigurerAspect本身需要由 Spring 配置(以便获得 对用于配置新对象的 Bean 工厂的引用)。如果你 使用基于 Java 的配置,可以添加@EnableSpringConfigured到任何@Configuration类,如下所示:spring-doc.cadn.net.cn

@Configuration
@EnableSpringConfigured
public class AppConfig {
}
@Configuration
@EnableSpringConfigured
class AppConfig {
}

如果您更喜欢基于 XML 的配置,则 SpringcontextNamespace定义了一个方便的context:spring-configured元素,您可以按如下方式使用它:spring-doc.cadn.net.cn

<context:spring-configured/>

实例@Configurable在配置方面之前创建的对象 导致向调试日志发出消息,并且没有配置 发生的对象。一个示例可能是 Spring 配置中的 bean,它创建了 domain 对象。在这种情况下,您可以使用depends-onbean 属性来手动指定 bean 依赖于 配置方面。以下示例演示如何使用depends-on属性:spring-doc.cadn.net.cn

<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 配置。spring-doc.cadn.net.cn

使用多个应用程序上下文

AnnotationBeanConfigurerAspect用于实现@Configurable支持 是 AspectJ 单例方面。单例方面的作用域与作用域相同 之staticmembers:每个方面实例有一个ClassLoader定义类型。 这意味着,如果您在同一个ClassLoader层次结构,您需要考虑在哪里定义@EnableSpringConfiguredbean 和 放置位置spring-aspects.jar在类路径上。spring-doc.cadn.net.cn

考虑一个典型的 Spring Web 应用程序配置,它有一个共享的父应用程序 定义通用业务服务的上下文,支持这些服务所需的一切, 以及每个 Servlet 的一个子应用程序上下文(其中包含特定的定义 到该 servlet)。所有这些上下文都共存于同一个环境中ClassLoader等级制度 因此,AnnotationBeanConfigurerAspect只能保存对其中一个的引用。 在这种情况下,我们建议定义@EnableSpringConfigured共享中的 bean (父)应用程序上下文。这定义了您可能想要的服务 注入到域对象中。结果是无法配置域对象 使用 @Configurable机制(无论如何,这可能不是你想做的事情)。spring-doc.cadn.net.cn

在同一容器中部署多个 Web 应用程序时,请确保每个 Web 应用程序加载spring-aspects.jar通过使用自己的ClassLoader(例如,通过将spring-aspects.jarWEB-INF/lib).如果spring-aspects.jar仅添加到容器范围的类路径(因此由共享父级ClassLoader),所有 Web 应用程序共享相同的 aspect 实例(这可能是 不是你想要的)。spring-doc.cadn.net.cn

AspectJ 的其他 Spring 方面

除了@Configurable方面spring-aspects.jar包含一个 AspectJ 可用于驱动 Spring 对类型和方法的事务管理的方面 用@Transactional注解。这主要适用于以下用户: 想要在 Spring 容器之外使用 Spring Framework 的事务支持。spring-doc.cadn.net.cn

解释的方面@Transactional注释是AnnotationTransactionAspect.使用此方面时,必须对 实现类(或该类中的方法或两者兼而有之),而不是接口(如果 any) 的类实现。AspectJ 遵循 Java 的规则,即对 接口不会继承。spring-doc.cadn.net.cn

一个@Transactional类上的注释指定 类中任何公共作的执行。spring-doc.cadn.net.cn

一个@Transactional类中方法的注释将覆盖默认值 类注释(如果存在)给出的交易语义。任何方法 可见性可以进行注释,包括私有方法。注释非公共方法 直接是获取执行此类方法的事务分界的唯一方法。spring-doc.cadn.net.cn

从 Spring Framework 4.2 开始,spring-aspects提供了一个类似的方面,提供 与标准完全相同的功能jakarta.transaction.Transactional注解。检查JtaAnnotationTransactionAspect了解更多详情。

对于想要使用 Spring 配置和事务的 AspectJ 程序员 管理支持,但不想(或不能)使用注释,spring-aspects.jar还包含abstract您可以扩展以提供自己的切入点的方面 定义。请参阅AbstractBeanConfigurerAspectAbstractTransactionAspect方面以获取更多信息。例如,以下内容 摘录显示了如何编写一个方面来配置对象的所有实例 使用与 完全限定的类名:spring-doc.cadn.net.cn

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条款) 由方面使用。spring-doc.cadn.net.cn

大多数 AspectJ 方面都是单例方面。这些的配置 方面很容易。您可以创建一个 bean 定义,该定义引用 aspect 类型为 normal 并包含factory-method="aspectOf"bean 属性。这确保了 Spring 通过向 AspectJ 请求它来获取 aspect 实例,而不是尝试创建 实例本身。以下示例演示如何使用factory-method="aspectOf"属性:spring-doc.cadn.net.cn

<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 运行时。spring-doc.cadn.net.cn

如果你有一些@AspectJ方面要用 AspectJ 编织(例如,对领域模型类型使用加载时编织)和其他你想要的@AspectJ方面与 Spring AOP 一起使用,并且这些方面都在 Spring 中配置,你需要告诉 Spring AOP @AspectJ自动代理支持的哪个确切子集配置中定义的@AspectJ方面应该用于自动代理。 您可以 为此,请使用一个或多个<include/>元素<aop:aspectj-autoproxy/>声明。每<include/>元素指定一个名称模式,并且只有带有 与至少一种模式匹配的名称用于 Spring AOP 自动代理 配置。以下示例演示如何使用<include/>元素:spring-doc.cadn.net.cn

<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-doc.cadn.net.cn

Spring Framework 为 AspectJ LTW 带来的价值在于实现了许多 对织造过程进行更细粒度的控制。“Vanilla”AspectJ LTW 是通过使用 Java (5+) 代理,通过在启动 JVM.因此,它是一个 JVM 范围的设置,在某些情况下可能没问题,但通常是 有点太粗糙了。启用 Spring 的 LTW 允许您在 每-ClassLoader基础,哪个粒度更细,哪个可以做更多 在“单 JVM-多应用程序”环境中的意义(例如在典型的 应用服务器环境)。spring-doc.cadn.net.cn

此外,在某些环境中,此支持使 加载时编织,而无需对应用程序服务器的启动进行任何修改 需要添加的脚本-javaagent:path/to/aspectjweaver.jar或(正如我们所描述的 在本节后面)-javaagent:path/to/spring-instrument.jar.开发人员配置 应用程序上下文,以启用加载时编织,而不是依赖管理员 通常负责部署配置(例如启动脚本)的人。spring-doc.cadn.net.cn

现在推销已经结束,让我们首先浏览一个 AspectJ 的快速示例 LTW 的 Spring,然后是 例。有关完整的示例,请参阅基于 Spring Framework 的 Petclinic 示例应用程序spring-doc.cadn.net.cn

第一个例子

假设您是一名应用程序开发人员,其任务是诊断系统中某些性能问题的原因。而不是打破一个分析工具,我们将打开一个简单的分析方面,让我们快速获得一些性能指标。然后我们可以应用更细粒度的分析工具立即应用于该特定区域。spring-doc.cadn.net.cn

此处提供的示例使用 XML 配置。您还可以配置和将@AspectJ与 Java 配置一起使用。具体来说,您可以使用@EnableLoadTimeWeaving注释作为替代<context:load-time-weaver/>(详情见下文)。

以下示例显示了分析方面,它并不花哨。它是一个基于时间的分析器,使用方面声明的@AspectJ样式:spring-doc.cadn.net.cn

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文件:spring-doc.cadn.net.cn

<!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文件添加到应用程序中的类中。好的 问题是它不需要很多配置(还有更多 您可以指定的选项,但稍后会详细介绍这些选项),如 以下示例:spring-doc.cadn.net.cn

<?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 实际效果的方法:spring-doc.cadn.net.cn

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前面显示的类:spring-doc.cadn.net.cn

java -javaagent:C:/projects/xyz/lib/spring-instrument.jar com.xyz.Main

-javaagent是用于指定和启用代理的标志 检测在 JVM 上运行的程序。Spring Framework 附带了这样的 agent,则InstrumentationSavingAgent,它打包在spring-instrument.jar作为-javaagent参数 前面的示例。spring-doc.cadn.net.cn

执行Main程序看起来像下一个例子。 (我已经介绍了一个Thread.sleep(..)语句添加到calculateEntitlement()实现,以便分析器实际捕获 0 以外的内容 毫秒(01234毫秒不是 AOP 引入的开销)。 以下列表显示了运行分析器时获得的输出:spring-doc.cadn.net.cn

Calculating entitlement

StopWatch 'ProfilingAspect': running time (millis) = 1234
------ ----- ----------------------------
ms     %     Task name
------ ----- ----------------------------
01234  100%  calculateEntitlement

由于此 LTW 是通过使用成熟的 AspectJ 来实现的,因此我们不仅限于提供建议 春豆。以下细微变化Main程序产生相同的结果 结果:spring-doc.cadn.net.cn

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-doc.cadn.net.cn

诚然,这个例子很简单。但是,Spring 中 LTW 支持的基础知识 在前面的示例中都介绍过,本节的其余部分将解释 详细说明每个配置和用法背后的“原因”。spring-doc.cadn.net.cn

ProfilingAspect这个例子中使用的可能是基本的,但它非常有用。这是一个 开发人员可以在开发过程中使用的开发时方面的一个很好的例子 然后轻松地从正在部署的应用程序的构建中排除 进入 UAT 或生产。

方面

您在 LTW 中使用的方面必须是 AspectJ 方面。你可以把它们写进去 要么是 AspectJ 语言本身,要么你可以用 @AspectJ 风格编写你的方面。 然后,您的切面都是有效的 AspectJ 和 Spring AOP 切面。 此外,编译后的方面类需要在类路径上可用。spring-doc.cadn.net.cn

META-INF/aop.xml

AspectJ LTW 基础结构使用一个或多个META-INF/aop.xml位于 Java 类路径上的文件(直接或更典型的是 jar 文件中)。 例如:spring-doc.cadn.net.cn

<!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,我们在这里不再进一步描述它。spring-doc.cadn.net.cn

所需库 (JARS)

至少,您需要以下库才能使用 Spring Framework 的支持 对于 AspectJ LTW:spring-doc.cadn.net.cn

弹簧配置

Spring LTW 支持的关键组件是LoadTimeWeaver接口(在org.springframework.instrument.classloadingpackage)和众多实现 其中与春季发行版一起发货。一个LoadTimeWeaver负责 添加一个或多个java.lang.instrument.ClassFileTransformers设置为ClassLoader在 运行时,它为各种有趣的应用程序打开了大门,其中之一 恰好是方面的 LTW。spring-doc.cadn.net.cn

如果您不熟悉运行时类文件转换的概念,请参阅 javadoc API 文档java.lang.instrument打包后再继续。 虽然该文档并不全面,但至少您可以看到关键接口 和类(供您阅读本节时参考)。

配置LoadTimeWeaver对于特定的ApplicationContext可以像 添加一行。(请注意,您几乎肯定需要使用ApplicationContext作为 Spring 容器 — 通常,一个BeanFactory莫 足够了,因为 LTW 支持BeanFactoryPostProcessors.)spring-doc.cadn.net.cn

要启用 Spring Framework 的 LTW 支持,您需要配置一个LoadTimeWeaver, 这通常是通过使用@EnableLoadTimeWeaving注释,如下所示:spring-doc.cadn.net.cn

@Configuration
@EnableLoadTimeWeaving
public class AppConfig {
}
@Configuration
@EnableLoadTimeWeaving
class AppConfig {
}

或者,如果您更喜欢基于 XML 的配置,请使用<context:load-time-weaver/>元素。请注意,该元素在contextNamespace。以下示例演示如何使用<context:load-time-weaver/>:spring-doc.cadn.net.cn

<?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,例如LoadTimeWeaverAspectJWeavingEnabler给你的。 默认值LoadTimeWeaverDefaultContextLoadTimeWeaver类,该类尝试 装饰自动检测到LoadTimeWeaver.的确切类型LoadTimeWeaver即“自动检测”取决于您的运行时环境。 下表总结了各种LoadTimeWeaver实现:spring-doc.cadn.net.cn

表 1.DefaultContextLoadTimeWeaver LoadTimeWeavers
运行时环境 LoadTimeWeaver实现

Apache Tomcat 中运行spring-doc.cadn.net.cn

TomcatLoadTimeWeaverspring-doc.cadn.net.cn

GlassFish 中运行(仅限于 EAR 部署)spring-doc.cadn.net.cn

GlassFishLoadTimeWeaverspring-doc.cadn.net.cn

在 Red Hat 的 JBoss ASWildFly 中运行spring-doc.cadn.net.cn

JBossLoadTimeWeaverspring-doc.cadn.net.cn

JVM 从 Spring 开始InstrumentationSavingAgent (java -javaagent:path/to/spring-instrument.jar)spring-doc.cadn.net.cn

InstrumentationLoadTimeWeaverspring-doc.cadn.net.cn

回退,期望底层 ClassLoader 遵循通用约定 (即addTransformer和可选的getThrowawayClassLoader方法)spring-doc.cadn.net.cn

ReflectiveLoadTimeWeaverspring-doc.cadn.net.cn

请注意,该表仅列出了LoadTimeWeavers当您 使用DefaultContextLoadTimeWeaver.您可以准确指定哪个LoadTimeWeaver实现以使用。spring-doc.cadn.net.cn

要指定特定的LoadTimeWeaver使用 Java 配置时,实现LoadTimeWeavingConfigurer接口并覆盖getLoadTimeWeaver()方法。 以下示例指定ReflectiveLoadTimeWeaver:spring-doc.cadn.net.cn

@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:spring-doc.cadn.net.cn

<?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.实际的ClassFileTransformerLTW 是ClassPreProcessorAgentAdapter(从 这org.aspectj.weaver.loadtimepackage) 类。请参阅ClassPreProcessorAgentAdapterclass 了解更多详细信息,因为具体如何 实际进行的编织超出了本文档的范围。spring-doc.cadn.net.cn

还有一个配置的最后一个属性需要讨论:aspectjWeaving属性(或aspectj-weaving如果您使用 XML)。此属性控制 LTW 是否启用。它接受三个可能的值之一,默认值为autodetect如果该属性不存在。下表总结了这三个 可能的值:spring-doc.cadn.net.cn

表 2.AspectJ 编织属性值
注释值 XML 值 解释

ENABLEDspring-doc.cadn.net.cn

onspring-doc.cadn.net.cn

AspectJ 编织已打开,并且根据需要在加载时编织 Aspect 。spring-doc.cadn.net.cn

DISABLEDspring-doc.cadn.net.cn

offspring-doc.cadn.net.cn

LTW 已关闭。加载时没有任何方面是编织的。spring-doc.cadn.net.cn

AUTODETECTspring-doc.cadn.net.cn

autodetectspring-doc.cadn.net.cn

如果 Spring LTW 基础设施可以找到至少一个META-INF/aop.xml文件 然后 AspectJ 编织开始。否则,它关闭。这是默认值。spring-doc.cadn.net.cn

特定于环境的配置

最后一部分包含您需要的任何其他设置和配置 当您在应用程序服务器和 Web 等环境中使用 Spring 的 LTW 支持时 器皿。spring-doc.cadn.net.cn

Tomcat、JBoss、WildFly

Tomcat 和 JBoss/WildFly 提供了一个通用的应用程序ClassLoader能够本地的 仪表。Spring 的原生 LTW 可能会利用这些 ClassLoader 实现 提供 AspectJ 编织。 如前所述,您可以简单地启用加载时编织。 具体来说,您不需要修改 JVM 启动脚本来添加-javaagent:path/to/spring-instrument.jar.spring-doc.cadn.net.cn

请注意,在 JBoss 上,您可能需要禁用应用程序服务器扫描以防止它 在应用程序实际启动之前加载类。快速解决方法是将 到您的工件中,一个名为WEB-INF/jboss-scanning.xml内容如下:spring-doc.cadn.net.cn

<scanning xmlns="urn:jboss:scanning:1.0"/>

通用 Java 应用程序

在不受支持的环境中需要类检测时 特定LoadTimeWeaver实现中,JVM 代理是通用解决方案。 对于这种情况,Spring 提供了InstrumentationLoadTimeWeaver这需要一个 特定于 Spring 的(但非常通用的)JVM 代理,spring-instrument.jar、自动检测 由 common@EnableLoadTimeWeaving<context:load-time-weaver/>设置。spring-doc.cadn.net.cn

要使用它,您必须通过提供 以下 JVM 选项:spring-doc.cadn.net.cn

-javaagent:/path/to/spring-instrument.jar

请注意,这需要修改 JVM 启动脚本,这可能会阻止您 在应用程序服务器环境中使用它(取决于您的服务器和您的 运营策略)。也就是说,对于每个 JVM 一个应用程序部署,例如独立 Spring Boot 应用程序,您通常在任何情况下都可以控制整个 JVM 设置。spring-doc.cadn.net.cn