到目前为止,我们在本章中介绍的所有内容都是纯粹的 Spring AOP。在本节中, 我们将了解如何使用 AspectJ 编译器或 weaver 而不是 or in 除了 Spring AOP 之外,如果您的需求超出了 Spring AOP 提供的功能 独自。

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

使用 AspectJ 依赖注入 Spring 的域对象

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

注释将类标记为符合 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) 与完全限定类型名称同名 ().由于 Bean 的默认名称是 其类型的完全限定名称,这是声明原型定义的便捷方式 省略该属性,如以下示例所示:Accountcom.xyz.domain.Accountid

<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 现在查找一个名为 Bean 的定义,并将其用作 定义以配置新实例。accountAccount

您还可以使用自动接线来避免在以下位置指定专用 Bean 定义 都。要让 Spring 应用自动接线,请使用注释的属性。您可以按类型或名称指定 or 用于自动接线, 分别。作为替代方法,最好指定显式的、注释驱动的 通过字段或方法级别或在字段或方法级别对 Bean 进行依赖注入(有关详细信息,请参阅基于注释的容器配置)。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“。在这种情况下, “初始化”是指新实例化的对象(例如,实例化的对象 与操作员)以及正在经历的对象 反序列化(例如,通过 readResolve())。AnnotationBeanConfigurerAspectspring-aspects.jar@ConfigurablenewSerializable

上一段中的一个关键词是“实质上”。在大多数情况下, “从新对象的初始化返回后”的确切语义是 好。在此上下文中,“初始化后”意味着依赖项是 在对象构建完成后注入。这意味着依赖项 不可用于类的构造函数主体。如果想要 在构造函数主体运行之前注入的依赖项,因此是 可用于构造函数的正文,您需要在声明中定义它,如下所示:@Configurable

  • Java

  • Kotlin

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

您可以找到有关各种切入点的语言语义的详细信息 在《AspectJ 编程指南的附录中键入 AspectJ。

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

  • Java

  • Kotlin

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

如果您更喜欢基于 XML 的配置,Spring 上下文命名空间定义了一个方便的元素,您可以按如下方式使用它:context:spring-configured

<context:spring-configured/>

在配置方面之前创建的对象的实例 导致向调试日志发出一条消息,并且没有配置 对象正在发生。一个示例可能是 Spring 配置中的 bean,它创建 domain 对象,当它被 Spring 初始化时。在这种情况下,您可以使用 bean 属性手动指定 Bean 依赖于 配置方面。下面的示例演示如何使用该属性:@Configurabledepends-ondepends-on

<bean id="myService"
		class="com.xyz.service.MyService"
		depends-on="org.springframework.beans.factory.aspectj.AnnotationBeanConfigurerAspect">

	<!-- ... -->

</bean>
不要通过 bean configurer 方面激活处理,除非您 真的意味着在运行时依赖它的语义。特别是,请确保您这样做 不适用于注册为常规 Spring Bean 的 Bean 类 与容器。这样做会导致双重初始化,一次通过 容器和一次通过方面。@Configurable@Configurable

单元测试对象@Configurable

支持的目标之一是启用独立的单元测试 域对象,而没有与硬编码查找相关的困难。 如果类型不是由 AspectJ 编织的,则注释没有影响 在单元测试期间。您可以在 测试并照常进行。如果类型是由 AspectJ 编织的, 您仍然可以像往常一样在容器外部进行单元测试,但您会看到一条警告 每次构造对象时的消息,指示它具有 Spring 未配置。@Configurable@Configurable@Configurable@Configurable

使用多个应用程序上下文

用于实现支持的 是 AspectJ 单例方面。单例方面的范围与范围相同 成员数:每个方面有一个定义类型的方面实例。 这意味着,如果在同一层次结构中定义多个应用程序上下文,则需要考虑在何处定义 Bean 和 在类路径上放置的位置。AnnotationBeanConfigurerAspect@ConfigurablestaticClassLoaderClassLoader@EnableSpringConfiguredspring-aspects.jar

考虑具有共享父应用程序的典型 Spring Web 应用程序配置 定义常见业务服务的上下文,支持这些服务所需的一切, 以及每个 Servlet 的一个子应用程序上下文(其中包含特定的定义 到该 servlet)。所有这些上下文都共存于同一层次结构中, 因此,只能保留对其中一个的引用。 在这种情况下,我们建议在共享中定义 Bean (父)应用程序上下文。这定义了您可能想要的服务 注入到域对象中。结果是无法配置域对象 引用在子(特定于 servlet)上下文中定义的 Bean,方法是使用 @Configurable机制(这可能不是你想做的事情)。ClassLoaderAnnotationBeanConfigurerAspect@EnableSpringConfigured

在同一容器中部署多个 Web 应用程序时,请确保每个 Web 应用程序使用自己的类型加载类型(例如,通过放置 )。如果仅添加到容器范围的类路径(因此由共享父级加载),则所有 Web 应用程序共享相同的方面实例(可能是 不是你想要的)。spring-aspects.jarClassLoaderspring-aspects.jarWEB-INF/libspring-aspects.jarClassLoader

AspectJ 的其他 Spring 方面

除了 aspect 之外,还包含一个 AspectJ 可用于驱动 Spring 的类型和方法事务管理的方面 用注释进行注释。这主要适用于以下用户 想要在 Spring 容器之外使用 Spring Framework 的事务支持。@Configurablespring-aspects.jar@Transactional

解释注释的方面是 。当你使用这个方面时,你必须注释 实现类(或该类中的方法或两者),而不是接口(如果 any) 实现。AspectJ 遵循 Java 的规则,即对 接口不会被继承。@TransactionalAnnotationTransactionAspect

类的注释指定了 类中任何公共操作的执行。@Transactional

类中方法的注释将覆盖缺省值 由类注解(如果存在)给出的事务语义。任何方法 可以对可见性进行注释,包括私有方法。注释非公共方法 直接是获取交易划分以执行此类方法的唯一方法。@Transactional

由于 Spring Framework 4.2 提供了一个类似的方面,它提供了 与标准注释完全相同的功能。查看更多详细信息。spring-aspectsjakarta.transaction.TransactionalJtaAnnotationTransactionAspect

对于想要使用 Spring 配置和事务的 AspectJ 程序员 管理支持,但不想(或不能)使用注释,还包含可以扩展以提供自己的切入点的方面 定义。有关详细信息,请参阅 和 方面的源代码。例如,以下内容 摘录显示了如何编写一个方面来配置对象的所有实例 在领域模型中使用与 完全限定的类名:spring-aspects.jarabstractAbstractBeanConfigurerAspectAbstractTransactionAspect

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 Aspects

当您在 Spring 应用程序中使用 AspectJ 方面时,很自然地想要和 期望能够使用 Spring 配置这些方面。AspectJ 运行时本身是 负责方面创建,以及配置 AspectJ 创建的方法 通过 Spring 的方面取决于 AspectJ 实例化模型(子句) 由方面使用。per-xxx

大多数 AspectJ 方面都是单例方面。这些的配置 方面很容易。您可以创建一个 Bean 定义,该定义将 aspect 类型引用为 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 定义,并在 ASPECT 实例创建 Bean 后,使用 FROM 支持来配置 ASPECT 实例 AspectJ 运行时。@Configurablespring-aspects.jar

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

<aop:aspectj-autoproxy>
	<aop:include name="thisBean"/>
	<aop:include name="thatBean"/>
</aop:aspectj-autoproxy>
不要被元素的名称所误导。使用它 导致创建 Spring AOP 代理。@AspectJ风格的方面 这里使用声明,但不涉及 AspectJ 运行时。<aop:aspectj-autoproxy/>

在 Spring 框架中使用 AspectJ 进行加载时编织

加载时间编织 (LTW) 是指将 AspectJ 方面编织成 应用程序的类文件,因为它们正在加载到 Java 虚拟机 (JVM) 中。 本节的重点是在特定上下文中配置和使用 LTW Spring 框架。本节不是对 LTW 的一般介绍。有关以下内容的完整详细信息 LTW 的细节和仅使用 AspectJ 配置 LTW(Spring 不是 完全涉及),请参阅 AspectJ 的 LTW 部分 开发环境指南

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

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

现在推销已经结束,让我们先来看看 AspectJ 的一个快速示例 使用 Spring 的 LTW,然后是有关 例。有关完整示例,请参阅 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() {
	}
}

我们还需要创建一个文件,以通知 AspectJ weaver 我们想把我们的融入到我们的课堂上。本文件约定,即 Java 类路径上存在一个名为 标准方面J。以下示例显示了该文件:META-INF/aop.xmlProfilingAspectMETA-INF/aop.xmlaop.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>
建议只编织特定的类(通常是 应用程序包,如上面的示例所示) 以避免 AspectJ 转储文件和警告等副作用。 从效率的角度来看,这也是一种最佳实践。aop.xml

现在,我们可以继续讨论配置中特定于 Spring 的部分。我们需要 配置 A(稍后解释)。这个加载时织机是 负责将方面配置编织成一个或 将更多文件添加到应用程序的类中。好的 问题是它不需要很多配置(还有更多 您可以指定的选项,但这些选项将在后面详细介绍),如 以下示例:LoadTimeWeaverMETA-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 配置)都已就绪,我们可以创建以下内容 驱动程序类,其中包含演示 LTW 实际操作的方法:META-INF/aop.xmlmain(..)

  • 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。 我们使用以下命令来运行前面所示的类:ClassLoaderMain

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

是用于指定和启用代理的标志 检测在 JVM 上运行的程序。Spring 框架附带了这样的 agent,它被打包在 that 中,作为 前面的示例。-javaagentInstrumentationSavingAgentspring-instrument.jar-javaagent

程序执行的输出类似于下一个示例。 (我在实现中引入了一个语句,以便探查器实际捕获 0 以外的内容 毫秒(毫秒不是 AOP 引入的开销)。 以下列表显示了我们在运行探查器时得到的输出:MainThread.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 容器和 然后创建一个完全外部的新实例 春天的背景。分析建议仍然被编织在一起。StubEntitlementCalculationService

诚然,这个例子过于简单化。但是,Spring 中 LTW 支持的基础知识 在前面的示例中都介绍了这些内容,本节的其余部分将进行说明 详细介绍每个配置和用法背后的“原因”。

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

方面

在 LTW 中使用的方面必须是 AspectJ 方面。你可以把它们写进去 要么是 AspectJ 语言本身,要么你可以用 @AspectJ 风格来编写你的方面。 然后,您的方面既是有效的 AspectJ 方面,也是 Spring AOP 方面。 此外,编译的方面类需要在类路径上可用。

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>
建议只编织特定的类(通常是 应用程序包,如上面的示例所示) 以避免 AspectJ 转储文件和警告等副作用。 从效率的角度来看,这也是一种最佳实践。aop.xml

此文件的结构和内容在 AspectJ 参考的 LTW 部分进行了详细介绍 文档。由于该文件是 100% AspectJ,因此我们在这里不再进一步描述。aop.xml

必需的库 (JARS)

至少需要以下库才能使用 Spring Framework 的支持 对于 AspectJ LTW:

  • spring-aop.jar

  • aspectjweaver.jar

如果使用 Spring 提供的代理来启用检测,则还需要:

  • spring-instrument.jar

Spring 配置

Spring 的 LTW 支持的关键组件是接口(在包中)和众多的实现 其中随 Spring 发行版一起发布。A 负责 向 AT 添加一个或多个 运行时,它为各种有趣的应用程序打开了大门,其中之一 恰好是方面的 LTW。LoadTimeWeaverorg.springframework.instrument.classloadingLoadTimeWeaverjava.lang.instrument.ClassFileTransformersClassLoader

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

为特定配置可以像 添加一行。(请注意,您几乎肯定需要使用 an 作为 Spring 容器——通常,a 不是 足够了,因为 LTW 支持使用 .)LoadTimeWeaverApplicationContextApplicationContextBeanFactoryBeanFactoryPostProcessors

要启用 Spring Framework 的 LTW 支持,您需要配置一个 , 这通常是通过使用注释来完成的,如下所示:LoadTimeWeaver@EnableLoadTimeWeaving

  • Java

  • Kotlin

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

或者,如果您更喜欢基于 XML 的配置,请使用该元素。请注意,该元素是在命名空间中定义的。以下示例演示如何使用:<context:load-time-weaver/>context<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,例如 A 和 An 。 默认值是类,它尝试 装饰自动检测到的 .“自动检测”的确切类型取决于您的运行时环境。 下表总结了各种实现:LoadTimeWeaverAspectJWeavingEnablerLoadTimeWeaverDefaultContextLoadTimeWeaverLoadTimeWeaverLoadTimeWeaverLoadTimeWeaver

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

Apache Tomcat 中运行

TomcatLoadTimeWeaver

GlassFish 中运行(仅限于 EAR 部署)

GlassFishLoadTimeWeaver

在 Red Hat 的 JBoss ASWildFly 中运行

JBossLoadTimeWeaver

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

InstrumentationLoadTimeWeaver

回退,期望基础 ClassLoader 遵循常见约定 (即和可选的方法)addTransformergetThrowawayClassLoader

ReflectiveLoadTimeWeaver

请注意,该表仅列出了以下情况下自动检测到的 使用 .您可以准确指定要使用的实现。LoadTimeWeaversDefaultContextLoadTimeWeaverLoadTimeWeaver

要指定特定的 Java 配置,请实现接口并重写该方法。 以下示例指定一个:LoadTimeWeaverLoadTimeWeavingConfigurergetLoadTimeWeaver()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>

由配置定义和注册的可以稍后 使用众所周知的名称从 Spring 容器中检索。 请记住,它仅作为 Spring 的 LTW 机制存在 基础结构来添加一个或多个 .执行 LTW 的实际值是 (从 package) 类。有关更多详细信息,请参阅该类的类级 javadoc,因为 织造实际影响超出了本文档的范围。LoadTimeWeaverloadTimeWeaverLoadTimeWeaverClassFileTransformersClassFileTransformerClassPreProcessorAgentAdapterorg.aspectj.weaver.loadtimeClassPreProcessorAgentAdapter

还有最后一个配置属性需要讨论:属性(或者如果使用 XML)。此属性控制 LTW 是否 是否启用。它接受三个可能的值之一,默认值为属性不存在时的值。下表总结了这三个 可能的值:aspectjWeavingaspectj-weavingautodetect

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

ENABLED

on

AspectJ 织造已开启,并根据需要在加载时织造方面。

DISABLED

off

LTW 已关闭。在加载时不会编织任何方面。

AUTODETECT

autodetect

如果 Spring LTW 基础结构至少可以找到一个文件, 然后 AspectJ 编织开启。否则,它将关闭。这是默认值。META-INF/aop.xml

特定于环境的配置

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

Tomcat、JBoss、WildFly

Tomcat 和 JBoss/WildFly 提供了一个通用的应用程序,它能够本地 仪表。Spring 的原生 LTW 可能会利用这些 ClassLoader 实现 提供 AspectJ 编织。 如前所述,您可以简单地启用加载时编织。 具体来说,您无需修改 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 代理,自动检测 通过通用和设置。LoadTimeWeaverInstrumentationLoadTimeWeaverspring-instrument.jar@EnableLoadTimeWeaving<context:load-time-weaver/>

要使用它,您必须通过提供 Spring 代理来启动虚拟机 以下 JVM 选项:

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

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