+ * @Configuration + * @EnableAspectJAutoProxy + * public class AppConfig { + * + * @Bean + * public FooService fooService() { + * return new FooService(); + * } + * + * @Bean + * public MyAspect myAspect() { + * return new MyAspect(); + * } + * }+ * + * 这里的{@code FooService}是一个典型的POJO组件,{@code MyAspect}是一个 + * {@code @Aspect}-风格的切面: + * + *
+ * public class FooService { + * + * // 各种方法 + * }+ * + *
+ * @Aspect + * public class MyAspect { + * + * @Before("execution(* FooService+.*(..))") + * public void advice() { + * // 适当地提供FooService方法的建议 + * } + * }+ * + * 在上述场景中,{@code @EnableAspectJAutoProxy}确保{@code MyAspect} + * 将被正确处理,并且{@code FooService}将被代理,混合其中的建议。 + * + *
用户可以通过{@link #proxyTargetClass()}属性控制为{@code FooService}创建的代理类型。 + * 以下示例启用了CGLIB风格的“子类”代理,而不是默认的基于接口的JDK代理方式。 + * + *
+ * @Configuration + * @EnableAspectJAutoProxy(proxyTargetClass=true) + * public class AppConfig { + * // ... + * }+ * + *
注意,{@code @Aspect} bean可以像任何其他组件一样进行组件扫描。 + * 只需将切面标记为{@code @Aspect}和{@code @Component}: + * + *
+ * package com.foo; + * + * @Component + * public class FooService { ... } + * + * @Aspect + * @Component + * public class MyAspect { ... }+ * + * 然后使用@{@link ComponentScan}注解来同时选择它们: + * + *
+ * @Configuration + * @ComponentScan("com.foo") + * @EnableAspectJAutoProxy + * public class AppConfig { + * + * // 不需要显式的{@code @Bean}定义 + * }+ * + * 注意:{@code @EnableAspectJAutoProxy}仅适用于其本地应用上下文, + * 允许在不同级别选择性地对bean进行代理。 + * 如果需要在多个级别应用其行为,例如常见的根Web应用程序上下文和任何单独的{@code DispatcherServlet}应用程序上下文中, + * 请在每个单独的上下文中重新声明{@code @EnableAspectJAutoProxy}。 + * + *
该功能要求类路径上存在{@code aspectjweaver}。
+ * 虽然{@code spring-aop}一般情况下对该依赖是可选的,但是对于{@code @EnableAspectJAutoProxy}及其基础设施,它是必需的。
+ *
+ * @author Chris Beams
+ * @author Juergen Hoeller
+ * @since 3.1
+ * @see org.aspectj.lang.annotation.Aspect
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Import(AspectJAutoProxyRegistrar.class)
+public @interface EnableAspectJAutoProxy {
+
+ /**
+ * 指示是否创建基于子类(CGLIB)的代理,而不是标准的基于Java接口的代理。默认值为{@code false}。
+ */
+ boolean proxyTargetClass() default false;
+
+ /**
+ * 指示代理是否应该被AOP框架公开为一个{@code ThreadLocal},以便通过{@link org.springframework.aop.framework.AopContext}类进行检索。
+ * 默认情况下关闭,即不保证{@code AopContext}访问将起作用。
+ * @since 4.3.1
+ */
+ boolean exposeProxy() default false;
+
+}
+```
+
+### 五、最佳实践
+
+使用`EnableAspectJAutoProxy`注解和Spring的基于注解的应用上下文来启用AspectJ自动代理功能。在程序中,首先创建了一个基于注解的应用上下文,然后通过该上下文获取了`FooService` bean,并调用了其方法。
+
+```java
+public class EnableAspectJAutoProxyDemo {
+
+ public static void main(String[] args) {
+ // 创建基于注解的应用上下文
+ AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
+ // 从应用上下文中获取FooService bean
+ FooService fooService = context.getBean(FooService.class);
+ // 调用FooService的方法
+ fooService.foo();
+ }
+}
+```
+
+通过`@Configuration`注解表示它是一个配置类,用于配置Spring应用的bean。其中,通过`@EnableAspectJAutoProxy`注解启用了AspectJ自动代理功能,使得Spring能够自动处理切面。在配置中定义了两个bean:`FooService`和`MyAspect`,分别用于创建`FooService`的实例和`MyAspect`切面的实例。
+
+```java
+@Configuration
+@EnableAspectJAutoProxy
+public class AppConfig {
+
+ @Bean
+ public FooService fooService() {
+ return new FooService();
+ }
+
+ @Bean
+ public MyAspect myAspect() {
+ return new MyAspect();
+ }
+}
+```
+
+`FooService`是一个简单的Java类,其中包含了一个名为`foo`的方法。
+
+```java
+public class FooService {
+
+ public void foo() {
+ System.out.println("foo");
+ }
+}
+```
+
+`MyAspect`是一个使用了`@Aspect`注解的Java类,表示它是一个切面。在这个类中,定义了一个名为`advice`的方法,并使用了`@Before`注解来指定在目标方法执行之前执行的通知。
+
+```java
+@Aspect
+public class MyAspect {
+
+ @Before("execution(* FooService+.*(..))")
+ public void advice() {
+ System.out.println("Before method execution");
+ }
+}
+```
+
+### 六、源码分析
+
+在`org.springframework.context.annotation.AspectJAutoProxyRegistrar#registerBeanDefinitions`方法中,首先注册了AspectJ注解自动代理创建器,然后获取了`@EnableAspectJAutoProxy`注解的属性。如果`@EnableAspectJAutoProxy`注解中指定了`proxyTargetClass`属性为true,则强制使用CGLIB代理;如果指定了`exposeProxy`属性为true,则强制代理对象暴露为ThreadLocal。
+
+```java
+@Override
+public void registerBeanDefinitions(
+ AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
+
+ // 1、注册AspectJ注解自动代理创建器
+ AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
+
+ // 获取@EnableAspectJAutoProxy注解的属性
+ AnnotationAttributes enableAspectJAutoProxy =
+ AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
+ if (enableAspectJAutoProxy != null) {
+ ///2.如果@EnableAspectJAutoProxy注解指定了proxyTargetClass属性为true,则强制使用CGLIB代理
+ if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
+ AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
+ }
+ // 3.如果@EnableAspectJAutoProxy注解指定了exposeProxy属性为true,则强制代理对象暴露为ThreadLocal
+ if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
+ AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
+ }
+ }
+}
+
+```
+
+在`org.springframework.aop.config.AopConfigUtils#registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry)`方法中,注册AspectJ注解自动代理创建器。它提供了一个重载方法,允许传入一个额外的参数,但这里调用的是没有额外参数的版本。在方法中,它会根据给定的`BeanDefinitionRegistry`对象来注册AspectJ注解自动代理创建器,并返回相应的BeanDefinition。
+
+```java
+@Nullable
+public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry) {
+ return registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry, null);
+}
+```
+
+在`org.springframework.aop.config.AopConfigUtils#registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry,source)`方法中,注册AspectJ注解自动代理创建器,并且可以指定源对象。在方法中,它调用了一个辅助方法`registerOrEscalateApcAsRequired`,该方法会根据需要注册或升级AspectJ注解自动代理创建器,并返回相应的BeanDefinition。
+
+```java
+@Nullable
+public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(
+ BeanDefinitionRegistry registry, @Nullable Object source) {
+
+ return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
+}
+```
+
+在`org.springframework.aop.config.AopConfigUtils#registerOrEscalateApcAsRequired`方法中,首先检查给定的`BeanDefinitionRegistry`中是否已经存在了自动代理创建器的定义。如果存在,则比较现有自动代理创建器与指定类的优先级,如果指定类的优先级更高,则进行升级操作;如果不存在,则创建一个新的自动代理创建器的定义,并将其注册到给定的`BeanDefinitionRegistry`中。
+
+```java
+@Nullable
+private static BeanDefinition registerOrEscalateApcAsRequired(
+ Class> cls, BeanDefinitionRegistry registry, @Nullable Object source) {
+
+ // 检查BeanDefinitionRegistry对象是否为null
+ Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
+
+ // 如果已经存在相同名称的自动代理创建器,则进行升级操作
+ if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
+ // 获取已存在的自动代理创建器的BeanDefinition
+ BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
+ // 比较已存在的自动代理创建器与指定类的优先级
+ if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
+ int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
+ int requiredPriority = findPriorityForClass(cls);
+ // 如果指定类的优先级高于已存在的自动代理创建器,则进行升级
+ if (currentPriority < requiredPriority) {
+ apcDefinition.setBeanClassName(cls.getName());
+ }
+ }
+ return null;
+ }
+
+ // 如果不存在相同名称的自动代理创建器,则进行注册操作
+ // 创建新的自动代理创建器的BeanDefinition
+ RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
+ beanDefinition.setSource(source);
+ beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
+ beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
+ // 注册新的自动代理创建器的BeanDefinition
+ registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
+ return beanDefinition;
+}
+```
+
+在`org.springframework.aop.config.AopConfigUtils#forceAutoProxyCreatorToUseClassProxying`方法中,强制自动代理创建器使用基于类的代理。它首先检查给定的`BeanDefinitionRegistry`中是否已经存在了自动代理创建器的定义。如果存在,则获取该定义并设置其`proxyTargetClass`属性为`true`,表示使用基于类的代理。
+
+```java
+public static void forceAutoProxyCreatorToUseClassProxying(BeanDefinitionRegistry registry) {
+ // 检查是否存在自动代理创建器的BeanDefinition
+ if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
+ // 获取自动代理创建器的BeanDefinition
+ BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
+ // 设置proxyTargetClass属性为true,表示使用基于类的代理
+ definition.getPropertyValues().add("proxyTargetClass", Boolean.TRUE);
+ }
+}
+```
+
+在`org.springframework.aop.config.AopConfigUtils#forceAutoProxyCreatorToExposeProxy`方法中,强制自动代理创建器暴露代理对象。它首先检查给定的`BeanDefinitionRegistry`中是否已经存在了自动代理创建器的定义。如果存在,则获取该定义并设置其`exposeProxy`属性为`true`,表示暴露代理对象。
+
+```java
+public static void forceAutoProxyCreatorToExposeProxy(BeanDefinitionRegistry registry) {
+ // 检查是否存在自动代理创建器的BeanDefinition
+ if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
+ // 获取自动代理创建器的BeanDefinition
+ BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
+ // 设置exposeProxy属性为true,表示暴露代理对象
+ definition.getPropertyValues().add("exposeProxy", Boolean.TRUE);
+ }
+}
+
+```
+
+### 七、常见问题
+
+1. **代理类型选择**
+ + 在@EnableAspectJAutoProxy注解中有一个proxyTargetClass属性,用于指定代理类型。如果选择了错误的代理类型,可能会导致意外的行为或错误。通常,选择默认的接口代理是比较安全的选择。
+
+2. **切面优先级问题**
+ + 如果同时存在多个切面,并且它们的通知方法匹配相同的连接点,可能会导致切面执行顺序不符合预期。可以通过实现Ordered接口或使用@Order注解来控制切面的执行顺序。
+
+3. **代理对象暴露问题**
+ + 在某些情况下,可能需要访问代理对象本身,例如在同一个类的不同方法中调用被代理的方法。这时,需要确保在@EnableAspectJAutoProxy注解中设置了exposeProxy属性为true,以便将代理对象暴露为ThreadLocal。
+
+4. **Spring配置问题**
++ 配置类中的@EnableAspectJAutoProxy注解可能会被错误地放置在不正确的位置,或者与其他注解冲突。因此,需要确保@EnableAspectJAutoProxy注解正确地放置在@Configuration类上,并且没有与其他注解冲突。
\ No newline at end of file
diff --git a/spring-aop/spring-aop-enableAspectJAutoProxy/pom.xml b/spring-aop/spring-aop-enableAspectJAutoProxy/pom.xml
new file mode 100644
index 0000000..b1efc2a
--- /dev/null
+++ b/spring-aop/spring-aop-enableAspectJAutoProxy/pom.xml
@@ -0,0 +1,14 @@
+
+ 要在 @{@link org.springframework.context.annotation.Configuration Configuration} 类上使用;
+ * 最简单的示例如下
+ *
+ * 要定制使用的 weaver,{@code @Configuration} 类可以实现 {@link LoadTimeWeavingConfigurer} 接口,并通过
+ * {@code #getLoadTimeWeaver} 方法返回一个自定义的 {@code LoadTimeWeaver} 实例
+ *
+ * 当使用 {@link EnableLoadTimeWeaving} 注解时,这个配置类会自动导入。
+ * 完整的使用详情请参阅 {@code @EnableLoadTimeWeaving} 的 javadoc。
+ *
+ * 作者Chris Beams
+ *
+ * @since 3.1
+ * @see LoadTimeWeavingConfigurer
+ * @see ConfigurableApplicationContext#LOAD_TIME_WEAVER_BEAN_NAME
+ */
+@Configuration(proxyBeanMethods = false)
+@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
+public class LoadTimeWeavingConfiguration implements ImportAware, BeanClassLoaderAware {
+
+ @Nullable
+ private AnnotationAttributes enableLTW;
+
+ @Nullable
+ private LoadTimeWeavingConfigurer ltwConfigurer;
+
+ @Nullable
+ private ClassLoader beanClassLoader;
+
+ @Override
+ public void setImportMetadata(AnnotationMetadata importMetadata) {
+ // 获取 @EnableLoadTimeWeaving 注解的属性
+ this.enableLTW = AnnotationConfigUtils.attributesFor(importMetadata, EnableLoadTimeWeaving.class);
+ if (this.enableLTW == null) {
+ throw new IllegalArgumentException(
+ "@EnableLoadTimeWeaving is not present on importing class " + importMetadata.getClassName());
+ }
+ }
+
+ @Autowired(required = false)
+ public void setLoadTimeWeavingConfigurer(LoadTimeWeavingConfigurer ltwConfigurer) {
+ // 设置用户自定义的 LoadTimeWeavingConfigurer 实例
+ this.ltwConfigurer = ltwConfigurer;
+ }
+
+ @Override
+ public void setBeanClassLoader(ClassLoader beanClassLoader) {
+ // 设置类加载器
+ this.beanClassLoader = beanClassLoader;
+ }
+
+ /**
+ * 注册 LoadTimeWeaver bean。
+ */
+ @Bean(name = ConfigurableApplicationContext.LOAD_TIME_WEAVER_BEAN_NAME)
+ @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
+ public LoadTimeWeaver loadTimeWeaver() {
+ Assert.state(this.beanClassLoader != null, "No ClassLoader set");
+ LoadTimeWeaver loadTimeWeaver = null;
+
+ if (this.ltwConfigurer != null) {
+ // 用户提供了自定义的 LoadTimeWeaver 实例
+ loadTimeWeaver = this.ltwConfigurer.getLoadTimeWeaver();
+ }
+
+ if (loadTimeWeaver == null) {
+ // 没有提供自定义的 LoadTimeWeaver -> 使用默认的
+ loadTimeWeaver = new DefaultContextLoadTimeWeaver(this.beanClassLoader);
+ }
+
+ if (this.enableLTW != null) {
+ // 获取启用 AspectJ 编织的配置
+ AspectJWeaving aspectJWeaving = this.enableLTW.getEnum("aspectjWeaving");
+ switch (aspectJWeaving) {
+ case DISABLED:
+ // AspectJ 编织被禁用 -> 什么也不做
+ break;
+ case AUTODETECT:
+ if (this.beanClassLoader.getResource(AspectJWeavingEnabler.ASPECTJ_AOP_XML_RESOURCE) == null) {
+ // 类路径上没有 aop.xml -> 视为 'disabled'
+ break;
+ }
+ // 类路径上有 aop.xml -> 启用编织
+ AspectJWeavingEnabler.enableAspectJWeaving(loadTimeWeaver, this.beanClassLoader);
+ break;
+ case ENABLED:
+ // 启用 AspectJ 编织
+ AspectJWeavingEnabler.enableAspectJWeaving(loadTimeWeaver, this.beanClassLoader);
+ break;
+ }
+ }
+
+ return loadTimeWeaver;
+ }
+}
+```
+
+`AspectJWeavingEnabler` 的后处理器,它实现了多个接口,包括 `BeanFactoryPostProcessor`、`BeanClassLoaderAware`、`LoadTimeWeaverAware` 和 `Ordered`。它的主要作用是在 Spring 应用程序上下文中注册 AspectJ 的 `ClassPreProcessorAgentAdapter`,并与默认的 `LoadTimeWeaver` 进行关联。其中,`enableAspectJWeaving` 方法用于启用 AspectJ 编织功能,而 `AspectJClassBypassingClassFileTransformer` 类则实现了一个用于绕过 AspectJ 类处理的装饰器,以避免潜在的链接错误。
+
+```java
+/**
+ * 后处理器,将 AspectJ 的 {@link org.aspectj.weaver.loadtime.ClassPreProcessorAgentAdapter}
+ * 注册到 Spring 应用程序上下文的默认 {@link org.springframework.instrument.classloading.LoadTimeWeaver} 中。
+ * 用于启用 AspectJ 的编织功能。
+ *
+ * 作者Juergen Hoeller,Ramnivas Laddad
+ *
+ * @since 2.5
+ */
+public class AspectJWeavingEnabler
+ implements BeanFactoryPostProcessor, BeanClassLoaderAware, LoadTimeWeaverAware, Ordered {
+
+ /**
+ * {@code aop.xml} 资源位置。
+ */
+ public static final String ASPECTJ_AOP_XML_RESOURCE = "META-INF/aop.xml";
+
+ @Nullable
+ private ClassLoader beanClassLoader;
+
+ @Nullable
+ private LoadTimeWeaver loadTimeWeaver;
+
+ @Override
+ public void setBeanClassLoader(ClassLoader classLoader) {
+ this.beanClassLoader = classLoader;
+ }
+
+ @Override
+ public void setLoadTimeWeaver(LoadTimeWeaver loadTimeWeaver) {
+ this.loadTimeWeaver = loadTimeWeaver;
+ }
+
+ @Override
+ public int getOrder() {
+ return HIGHEST_PRECEDENCE;
+ }
+
+ @Override
+ public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
+ // 启用 AspectJ 编织
+ enableAspectJWeaving(this.loadTimeWeaver, this.beanClassLoader);
+ }
+
+ /**
+ * 使用给定的 {@link LoadTimeWeaver} 启用 AspectJ 编织。
+ *
+ * @param weaverToUse 要应用的 LoadTimeWeaver(或 {@code null} 表示使用默认的 weaver)
+ * @param beanClassLoader 如果需要,为其创建默认 weaver 的类加载器
+ */
+ public static void enableAspectJWeaving(
+ @Nullable LoadTimeWeaver weaverToUse, @Nullable ClassLoader beanClassLoader) {
+ if (weaverToUse == null) {
+ if (InstrumentationLoadTimeWeaver.isInstrumentationAvailable()) {
+ // 如果可以使用 Instrumentation,创建 InstrumentationLoadTimeWeaver
+ weaverToUse = new InstrumentationLoadTimeWeaver(beanClassLoader);
+ }
+ else {
+ throw new IllegalStateException("No LoadTimeWeaver available");
+ }
+ }
+ // 添加一个 ClassFileTransformer,用于绕过 AspectJ 类的处理,以避免潜在的 LinkageErrors
+ weaverToUse.addTransformer(
+ new AspectJClassBypassingClassFileTransformer(new ClassPreProcessorAgentAdapter()));
+ }
+
+ /**
+ * ClassFileTransformer 的装饰器,用于禁止处理 AspectJ 类,以避免潜在的 LinkageErrors。
+ *
+ * @see org.springframework.context.annotation.LoadTimeWeavingConfiguration
+ */
+ private static class AspectJClassBypassingClassFileTransformer implements ClassFileTransformer {
+
+ private final ClassFileTransformer delegate;
+
+ public AspectJClassBypassingClassFileTransformer(ClassFileTransformer delegate) {
+ this.delegate = delegate;
+ }
+
+ @Override
+ public byte[] transform(ClassLoader loader, String className, Class> classBeingRedefined,
+ ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
+ if (className.startsWith("org.aspectj") || className.startsWith("org/aspectj")) {
+ // 如果是 AspectJ 类,则直接返回原始字节码
+ return classfileBuffer;
+ }
+ // 否则,调用委托的 ClassFileTransformer 处理字节码
+ return this.delegate.transform(loader, className, classBeingRedefined, protectionDomain, classfileBuffer);
+ }
+ }
+}
+```
+
+### 七、常见问题
+
+1. **AspectJ Weaver 未配置或不可用**
+
+ + 如果未正确配置 AspectJ Weaver,或者 Weaver 不可用,可能导致加载时编织无法正常工作。这通常需要检查项目的依赖配置和 AspectJ Weaver 的集成情况。
+
+2. **ClassLoader 冲突**
+
+ + 在复杂的应用程序中,可能会存在 ClassLoader 冲突,特别是当使用多个模块或库时。这可能会影响加载时编织的行为,导致意外的结果。
+
+3. **AOP 切面匹配问题**
+
+ + 当使用 AspectJ 编写切面时,可能会遇到切面匹配不准确或不完全的问题。这可能需要检查切面表达式以确保其正确匹配目标方法或类。
+
+4. **调试困难**
+
+ + 在某些情况下,加载时编织可能会导致调试困难,特别是当与其他 AOP 技术或动态代理结合使用时。在调试时,可能需要额外的注意和技巧来定位和解决问题。
+
+5. **版本兼容性问题**
+
+ + 加载时编织的实现可能与 Spring Framework 或 AspectJ 的特定版本不兼容,这可能会导致运行时错误或不一致的行为。在使用时需要注意版本兼容性,并确保选择适当的版本。
\ No newline at end of file
diff --git a/spring-aop/spring-aop-enableLoadTimeWeaving/pom.xml b/spring-aop/spring-aop-enableLoadTimeWeaving/pom.xml
new file mode 100644
index 0000000..e2418c9
--- /dev/null
+++ b/spring-aop/spring-aop-enableLoadTimeWeaving/pom.xml
@@ -0,0 +1,28 @@
+
+
+ * @Configuration
+ * @EnableLoadTimeWeaving
+ * public class AppConfig {
+ *
+ * // 应用特定的 @Bean 定义...
+ * }
+ *
+ * 上面的示例等价于以下的 Spring XML 配置
+ *
+ *
+ * <beans>
+ *
+ * <context:load-time-weaver/>
+ *
+ * <!-- 应用特定的 <bean> 定义 -->
+ *
+ * </beans>
+ *
+ *
+ * {@code LoadTimeWeaverAware} 接口
+ * 任何实现 {@link org.springframework.context.weaving.LoadTimeWeaverAware LoadTimeWeaverAware} 接口的 bean
+ * 都将自动接收到 {@code LoadTimeWeaver} 引用;例如,Spring 的 JPA 启动支持。
+ *
+ * 定制 {@code LoadTimeWeaver}
+ * 默认的 weaver 将自动确定参见 {@link DefaultContextLoadTimeWeaver}。
+ *
+ *
+ * @Configuration
+ * @EnableLoadTimeWeaving
+ * public class AppConfig implements LoadTimeWeavingConfigurer {
+ *
+ * @Override
+ * public LoadTimeWeaver getLoadTimeWeaver() {
+ * MyLoadTimeWeaver ltw = new MyLoadTimeWeaver();
+ * ltw.addClassTransformer(myClassFileTransformer);
+ * // ...
+ * return ltw;
+ * }
+ * }
+ *
+ * 上面的示例可与以下 Spring XML 配置进行比较
+ *
+ *
+ * <beans>
+ *
+ * <context:load-time-weaver weaverClass="com.acme.MyLoadTimeWeaver"/>
+ *
+ * </beans>
+ *
+ *
+ * 代码示例与 XML 示例的区别在于它实际上实例化了 {@code MyLoadTimeWeaver} 类型,这意味着它还可以配置实例,
+ * 例如调用 {@code #addClassTransformer} 方法。这展示了基于代码的配置方法通过直接编程访问更加灵活。
+ *
+ * 启用基于 AspectJ 的编织
+ * 可通过 {@link #aspectjWeaving()} 属性启用 AspectJ 加载时编织,这将导致通过 {@link LoadTimeWeaver#addTransformer}
+ * 注册 {@linkplain org.aspectj.weaver.loadtime.ClassPreProcessorAgentAdapter AspectJ 类转换器}。如果类路径中存在
+ * "META-INF/aop.xml" 资源,则默认情况下将激活 AspectJ 编织。示例
+ *
+ *
+ * @Configuration
+ * @EnableLoadTimeWeaving(aspectjWeaving=ENABLED)
+ * public class AppConfig {
+ * }
+ *
+ * 上面的示例可与以下 Spring XML 配置进行比较
+ *
+ *
+ * <beans>
+ *
+ * <context:load-time-weaver aspectj-weaving="on"/>
+ *
+ * </beans>
+ *
+ *
+ * 这两个示例是等价的,但有一个重要的例外在 XML 的情况下,当 {@code aspectj-weaving} 是 "on" 时,
+ * {@code