# 5.10在Spring应用程序中使用AspectJ

到目前为止，我们在这一章中介绍的所有内容都是纯Spring AOP。在本节中，如果你要使用Spring AOP之外的功能，我们将介绍如何在Spring中使用AspectJ编译器或weaver。

Spring附带了一个小的AspectJ方面库，它在你的发行版中作为spring-aspects.jar单独提供。为了使用类路径中的方面，需要将其添加到类路径中。“使用aspectj来依赖注入带有Spring的域对象”，以及“aspectj的其他Spring方面”，讨论了这个库的内容以及如何使用它。“通过使用SpringIOC来配置AspectJ方面”讨论了如何使用AspectJ编译器对AspectJ方面进行依赖注入。最后，“在Spring框架中使用AspectJ进行加载时编织”，为使用AspectJ的Spring应用程序提供了加载时编织的介绍。

## 5.10.1 使用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实例（这可能不是你想要的）。

## 5.10.2 AspectJ的其他Spring aspects

除了@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);
}
```

## 5.10.3使用SpringIoC来配置AspectJ Aspects

当你将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运行时。

## 5.10.4 Spring框架中AspectJ的Load-time Weaving

加载时间编织（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-aop.jar
* aspectjweaver.jar

如果使用Spring提供的代理来启用检测，还需要：

* spring-instrument.jar

**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设置。
