diff --git a/README.md b/README.md index da82b2d..6c427d3 100644 --- a/README.md +++ b/README.md @@ -210,6 +210,8 @@ - [Advisor](spring-aop/spring-aop-advisor/README.md):用于将通知和切点结合,实现切面编程的横切关注点。 - [AdvisorAdapter](spring-aop/spring-aop-advisorAdapter/README.md):适配不同类型通知到拦截器链。 - [TargetSource](spring-aop/spring-aop-targetSource/README.md):管理AOP代理对象的获取与释放。 + - [@EnableAspectJAutoProxy](spring-aop/spring-aop-enableAspectJAutoProxy/README,md):启用AspectJ切面自动代理。 + - [@EnableLoadTimeWeaving](spring-aop/spring-aop-enableLoadTimeWeaving/README.md):启用Spring加载时编织。 + Spring AOT diff --git a/spring-aop/pom.xml b/spring-aop/pom.xml index 73ccd25..1255037 100644 --- a/spring-aop/pom.xml +++ b/spring-aop/pom.xml @@ -18,16 +18,16 @@ spring-aop-methodMatcher spring-aop-jdkProxy spring-aop-cglibProxy - spring-aop-annotation-aspect - spring-aop-annotation-pointcut - spring-aop-annotation-around - spring-aop-annotation-before - spring-aop-annotation-after - spring-aop-annotation-afterReturning - spring-aop-annotation-afterThrowing - spring-aop-annotation-declareParents - spring-aop-annotation-enableAspectJAutoProxy - spring-aop-annotation-enableLoadTimeWeaving + spring-aop-aspectj-aspect + spring-aop-aspectj-pointcut + spring-aop-aspectj-around + spring-aop-aspectj-before + spring-aop-aspectj-after + spring-aop-aspectj-afterReturning + spring-aop-aspectj-afterThrowing + spring-aop-aspectj-declareParents + spring-aop-enableAspectJAutoProxy + spring-aop-enableLoadTimeWeaving spring-aop-advice-methodInterceptor spring-aop-advice-methodBeforeAdvice spring-aop-advice-afterReturningAdvice diff --git a/spring-aop/spring-aop-annotation-afterReturning/pom.xml b/spring-aop/spring-aop-annotation-afterReturning/pom.xml deleted file mode 100644 index 88d625b..0000000 --- a/spring-aop/spring-aop-annotation-afterReturning/pom.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - com.xcs.spring - spring-aop - 0.0.1-SNAPSHOT - - - 4.0.0 - spring-aop-annotation-afterReturning - - \ No newline at end of file diff --git a/spring-aop/spring-aop-annotation-afterThrowing/pom.xml b/spring-aop/spring-aop-annotation-afterThrowing/pom.xml deleted file mode 100644 index 566196a..0000000 --- a/spring-aop/spring-aop-annotation-afterThrowing/pom.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - com.xcs.spring - spring-aop - 0.0.1-SNAPSHOT - - - 4.0.0 - spring-aop-annotation-afterThrowing - - \ No newline at end of file diff --git a/spring-aop/spring-aop-annotation-declareParents/pom.xml b/spring-aop/spring-aop-annotation-declareParents/pom.xml deleted file mode 100644 index 5495e07..0000000 --- a/spring-aop/spring-aop-annotation-declareParents/pom.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - com.xcs.spring - spring-aop - 0.0.1-SNAPSHOT - - - 4.0.0 - spring-aop-annotation-declareParents - - \ No newline at end of file diff --git a/spring-aop/spring-aop-annotation-enableAspectJAutoProxy/pom.xml b/spring-aop/spring-aop-annotation-enableAspectJAutoProxy/pom.xml deleted file mode 100644 index 912e4b4..0000000 --- a/spring-aop/spring-aop-annotation-enableAspectJAutoProxy/pom.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - com.xcs.spring - spring-aop - 0.0.1-SNAPSHOT - - - 4.0.0 - spring-aop-annotation-enableAspectJAutoProxy - - \ No newline at end of file diff --git a/spring-aop/spring-aop-annotation-enableLoadTimeWeaving/pom.xml b/spring-aop/spring-aop-annotation-enableLoadTimeWeaving/pom.xml deleted file mode 100644 index 8ff9dfd..0000000 --- a/spring-aop/spring-aop-annotation-enableLoadTimeWeaving/pom.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - com.xcs.spring - spring-aop - 0.0.1-SNAPSHOT - - - 4.0.0 - spring-aop-annotation-enableLoadTimeWeaving - - \ No newline at end of file diff --git a/spring-aop/spring-aop-annotation-pointcut/pom.xml b/spring-aop/spring-aop-annotation-pointcut/pom.xml deleted file mode 100644 index 68f4154..0000000 --- a/spring-aop/spring-aop-annotation-pointcut/pom.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - com.xcs.spring - spring-aop - 0.0.1-SNAPSHOT - - - 4.0.0 - spring-aop-annotation-pointcut - - \ No newline at end of file diff --git a/spring-aop/spring-aop-annotation-after/pom.xml b/spring-aop/spring-aop-aspectj-after/pom.xml similarity index 87% rename from spring-aop/spring-aop-annotation-after/pom.xml rename to spring-aop/spring-aop-aspectj-after/pom.xml index f687832..81ab7b8 100644 --- a/spring-aop/spring-aop-annotation-after/pom.xml +++ b/spring-aop/spring-aop-aspectj-after/pom.xml @@ -9,6 +9,6 @@ 4.0.0 - spring-aop-annotation-after + spring-aop-aspectj-after \ No newline at end of file diff --git a/spring-aop/spring-aop-aspectj-afterReturning/pom.xml b/spring-aop/spring-aop-aspectj-afterReturning/pom.xml new file mode 100644 index 0000000..41cabb8 --- /dev/null +++ b/spring-aop/spring-aop-aspectj-afterReturning/pom.xml @@ -0,0 +1,14 @@ + + + + com.xcs.spring + spring-aop + 0.0.1-SNAPSHOT + + + 4.0.0 + spring-aop-aspectj-afterReturning + + \ No newline at end of file diff --git a/spring-aop/spring-aop-aspectj-afterThrowing/pom.xml b/spring-aop/spring-aop-aspectj-afterThrowing/pom.xml new file mode 100644 index 0000000..3da69cf --- /dev/null +++ b/spring-aop/spring-aop-aspectj-afterThrowing/pom.xml @@ -0,0 +1,14 @@ + + + + com.xcs.spring + spring-aop + 0.0.1-SNAPSHOT + + + 4.0.0 + spring-aop-aspectj-afterThrowing + + \ No newline at end of file diff --git a/spring-aop/spring-aop-annotation-aspect/pom.xml b/spring-aop/spring-aop-aspectj-around/pom.xml similarity index 87% rename from spring-aop/spring-aop-annotation-aspect/pom.xml rename to spring-aop/spring-aop-aspectj-around/pom.xml index 1aee724..9eb5db2 100644 --- a/spring-aop/spring-aop-annotation-aspect/pom.xml +++ b/spring-aop/spring-aop-aspectj-around/pom.xml @@ -9,6 +9,6 @@ 4.0.0 - spring-aop-annotation-aspect + spring-aop-aspectj-around \ No newline at end of file diff --git a/spring-aop/spring-aop-aspectj-aspect/README.md b/spring-aop/spring-aop-aspectj-aspect/README.md new file mode 100644 index 0000000..e6c562d --- /dev/null +++ b/spring-aop/spring-aop-aspectj-aspect/README.md @@ -0,0 +1,31 @@ +## Aspect + +- [Aspect](#Aspect) + - [一、基本信息](#一基本信息) + - [二、基本描述](#二基本描述) + - [三、主要功能](#三主要功能) + - [四、接口源码](#四接口源码) + - [五、主要实现](#五主要实现) + - [六、最佳实践](#六最佳实践) + - [七、源码分析](#七源码分析) + - [八、常见问题](#八常见问题) + +### 一、基本信息 + +✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) + +### 二、基本描述 + + + +### 三、主要功能 + +### 四、接口源码 + +### 五、主要实现 + +### 六、最佳实践 + +### 七、源码分析 + +### 八、常见问题 \ No newline at end of file diff --git a/spring-aop/spring-aop-annotation-before/pom.xml b/spring-aop/spring-aop-aspectj-aspect/pom.xml similarity index 87% rename from spring-aop/spring-aop-annotation-before/pom.xml rename to spring-aop/spring-aop-aspectj-aspect/pom.xml index fccb018..e1e8bb5 100644 --- a/spring-aop/spring-aop-annotation-before/pom.xml +++ b/spring-aop/spring-aop-aspectj-aspect/pom.xml @@ -9,6 +9,6 @@ 4.0.0 - spring-aop-annotation-before + spring-aop-aspectj-aspect \ No newline at end of file diff --git a/spring-aop/spring-aop-aspectj-aspect/src/main/java/com/xcs/spring/AppConfig.java b/spring-aop/spring-aop-aspectj-aspect/src/main/java/com/xcs/spring/AppConfig.java new file mode 100644 index 0000000..23375bb --- /dev/null +++ b/spring-aop/spring-aop-aspectj-aspect/src/main/java/com/xcs/spring/AppConfig.java @@ -0,0 +1,11 @@ +package com.xcs.spring; + +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.EnableAspectJAutoProxy; + +@Configuration +@EnableAspectJAutoProxy +@ComponentScan(basePackages = "com.xcs.spring") +public class AppConfig { +} diff --git a/spring-aop/spring-aop-aspectj-aspect/src/main/java/com/xcs/spring/AspectDemo.java b/spring-aop/spring-aop-aspectj-aspect/src/main/java/com/xcs/spring/AspectDemo.java new file mode 100644 index 0000000..b812ae2 --- /dev/null +++ b/spring-aop/spring-aop-aspectj-aspect/src/main/java/com/xcs/spring/AspectDemo.java @@ -0,0 +1,13 @@ +package com.xcs.spring; + +import com.xcs.spring.service.MyService; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; + +public class AspectDemo { + public static void main(String[] args) { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); + MyService service = context.getBean(MyService.class); + System.out.println("service.getClass() = " + service.getClass()); + service.doSomething(); + } +} diff --git a/spring-aop/spring-aop-aspectj-aspect/src/main/java/com/xcs/spring/LoggingAspect.java b/spring-aop/spring-aop-aspectj-aspect/src/main/java/com/xcs/spring/LoggingAspect.java new file mode 100644 index 0000000..109fa4a --- /dev/null +++ b/spring-aop/spring-aop-aspectj-aspect/src/main/java/com/xcs/spring/LoggingAspect.java @@ -0,0 +1,15 @@ +package com.xcs.spring; + +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.springframework.stereotype.Component; + +@Aspect +@Component +class LoggingAspect { + + @Before("execution(* com.xcs.spring.service.MyService.doSomething(..))") + public void beforeAdvice() { + System.out.println("Before executing the method..." ); + } +} diff --git a/spring-aop/spring-aop-aspectj-aspect/src/main/java/com/xcs/spring/service/MyService.java b/spring-aop/spring-aop-aspectj-aspect/src/main/java/com/xcs/spring/service/MyService.java new file mode 100644 index 0000000..928bc15 --- /dev/null +++ b/spring-aop/spring-aop-aspectj-aspect/src/main/java/com/xcs/spring/service/MyService.java @@ -0,0 +1,10 @@ +package com.xcs.spring.service; + +import org.springframework.stereotype.Service; + +@Service +public class MyService { + public void doSomething() { + System.out.println("Doing something..."); + } +} diff --git a/spring-aop/spring-aop-annotation-around/pom.xml b/spring-aop/spring-aop-aspectj-before/pom.xml similarity index 87% rename from spring-aop/spring-aop-annotation-around/pom.xml rename to spring-aop/spring-aop-aspectj-before/pom.xml index ee6f5ee..3bb391a 100644 --- a/spring-aop/spring-aop-annotation-around/pom.xml +++ b/spring-aop/spring-aop-aspectj-before/pom.xml @@ -9,6 +9,6 @@ 4.0.0 - spring-aop-annotation-around + spring-aop-aspectj-before \ No newline at end of file diff --git a/spring-aop/spring-aop-aspectj-declareParents/pom.xml b/spring-aop/spring-aop-aspectj-declareParents/pom.xml new file mode 100644 index 0000000..c4f0e11 --- /dev/null +++ b/spring-aop/spring-aop-aspectj-declareParents/pom.xml @@ -0,0 +1,14 @@ + + + + com.xcs.spring + spring-aop + 0.0.1-SNAPSHOT + + + 4.0.0 + spring-aop-aspectj-declareParents + + \ No newline at end of file diff --git a/spring-aop/spring-aop-aspectj-pointcut/pom.xml b/spring-aop/spring-aop-aspectj-pointcut/pom.xml new file mode 100644 index 0000000..cac648c --- /dev/null +++ b/spring-aop/spring-aop-aspectj-pointcut/pom.xml @@ -0,0 +1,14 @@ + + + + com.xcs.spring + spring-aop + 0.0.1-SNAPSHOT + + + 4.0.0 + spring-aop-aspectj-pointcut + + \ No newline at end of file diff --git a/spring-aop/spring-aop-enableAspectJAutoProxy/README,md b/spring-aop/spring-aop-enableAspectJAutoProxy/README,md new file mode 100644 index 0000000..874335d --- /dev/null +++ b/spring-aop/spring-aop-enableAspectJAutoProxy/README,md @@ -0,0 +1,347 @@ +## @EnableAspectJAutoProxy + +- [@EnableAspectJAutoProxy](#@EnableAspectJAutoProxy) + - [一、基本信息](#一基本信息) + - [二、基本描述](#二基本描述) + - [三、主要功能](#三主要功能) + - [四、注解源码](#注解源码) + - [五、最佳实践](#五最佳实践) + - [六、源码分析](#六源码分析) + - [七、常见问题](#七常见问题) + +### 一、基本信息 + +✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) + +### 二、基本描述 + +`EnableAspectJAutoProxy`注解是Spring框架中的一个注解,用于启用AspectJ自动代理功能,它能够自动将AspectJ切面与Spring的IoC容器集成,无需显式配置大量AOP相关的内容,从而简化AOP的使用和配置。 + +### 三、主要功能 + +1. **启用AspectJ自动代理功能** + + + 通过在配置类上添加该注解,Spring会自动启用AspectJ自动代理功能,无需显式配置大量AOP相关内容。 + +2. **自动创建代理对象** + + + Spring会自动创建代理对象来应用切面,将切面逻辑与目标对象进行结合。 + +3. **集成AspectJ切面与Spring容器** + + + 能够方便地将AspectJ切面与Spring的IoC容器集成,实现横切关注点的模块化管理。 + +4. **简化AOP配置** + + + 减少了手动配置AOP所需的繁琐步骤,提高了开发效率。 + +5. **自动扫描切面** + + + Spring会自动扫描应用中的AspectJ切面,并将其应用到相应的目标对象中,无需手动配置切面。 + +### 四、注解源码 + +`EnableAspectJAutoProxy`注解,用于启用支持处理使用AspectJ的`@Aspect`注解标记的组件,在Spring中类似于XML配置中的``功能。它允许通过配置类轻松集成AspectJ切面,并控制代理类型和代理的可见性。 + +```java +/** + * 启用支持处理使用AspectJ的{@code @Aspect}注解标记的组件, + * 类似于Spring的{@code } XML元素中的功能。 + * 应用在如下的@{@link Configuration}类上: + * + *
+ * @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 @@ + + + + com.xcs.spring + spring-aop + 0.0.1-SNAPSHOT + + + 4.0.0 + spring-aop-enableAspectJAutoProxy + + \ No newline at end of file diff --git a/spring-aop/spring-aop-enableAspectJAutoProxy/src/main/java/com/xcs/spring/AppConfig.java b/spring-aop/spring-aop-enableAspectJAutoProxy/src/main/java/com/xcs/spring/AppConfig.java new file mode 100644 index 0000000..1543175 --- /dev/null +++ b/spring-aop/spring-aop-enableAspectJAutoProxy/src/main/java/com/xcs/spring/AppConfig.java @@ -0,0 +1,20 @@ +package com.xcs.spring; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.EnableAspectJAutoProxy; + +@Configuration +@EnableAspectJAutoProxy +public class AppConfig { + + @Bean + public FooService fooService() { + return new FooService(); + } + + @Bean + public MyAspect myAspect() { + return new MyAspect(); + } +} diff --git a/spring-aop/spring-aop-enableAspectJAutoProxy/src/main/java/com/xcs/spring/EnableAspectJAutoProxyDemo.java b/spring-aop/spring-aop-enableAspectJAutoProxy/src/main/java/com/xcs/spring/EnableAspectJAutoProxyDemo.java new file mode 100644 index 0000000..ebfb589 --- /dev/null +++ b/spring-aop/spring-aop-enableAspectJAutoProxy/src/main/java/com/xcs/spring/EnableAspectJAutoProxyDemo.java @@ -0,0 +1,15 @@ +package com.xcs.spring; + +import org.springframework.context.annotation.AnnotationConfigApplicationContext; + +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(); + } +} \ No newline at end of file diff --git a/spring-aop/spring-aop-enableAspectJAutoProxy/src/main/java/com/xcs/spring/FooService.java b/spring-aop/spring-aop-enableAspectJAutoProxy/src/main/java/com/xcs/spring/FooService.java new file mode 100644 index 0000000..b4295b3 --- /dev/null +++ b/spring-aop/spring-aop-enableAspectJAutoProxy/src/main/java/com/xcs/spring/FooService.java @@ -0,0 +1,8 @@ +package com.xcs.spring; + +public class FooService { + + public void foo() { + System.out.println("foo"); + } +} diff --git a/spring-aop/spring-aop-enableAspectJAutoProxy/src/main/java/com/xcs/spring/MyAspect.java b/spring-aop/spring-aop-enableAspectJAutoProxy/src/main/java/com/xcs/spring/MyAspect.java new file mode 100644 index 0000000..fd434c3 --- /dev/null +++ b/spring-aop/spring-aop-enableAspectJAutoProxy/src/main/java/com/xcs/spring/MyAspect.java @@ -0,0 +1,13 @@ +package com.xcs.spring; + +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; + +@Aspect +public class MyAspect { + + @Before("execution(* FooService+.*(..))") + public void advice() { + System.out.println("Before method execution"); + } +} diff --git a/spring-aop/spring-aop-enableLoadTimeWeaving/README.md b/spring-aop/spring-aop-enableLoadTimeWeaving/README.md new file mode 100644 index 0000000..932e68b --- /dev/null +++ b/spring-aop/spring-aop-enableLoadTimeWeaving/README.md @@ -0,0 +1,491 @@ +## @EnableLoadTimeWeaving + +- [@EnableLoadTimeWeaving](#@EnableLoadTimeWeaving) + - [一、基本信息](#一基本信息) + - [二、基本描述](#二基本描述) + - [三、主要功能](#三主要功能) + - [四、注解源码](#注解源码) + - [五、最佳实践](#五最佳实践) + - [六、源码分析](#六源码分析) + - [七、常见问题](#七常见问题) + +### 一、基本信息 + +✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) + +### 二、基本描述 + +`@EnableLoadTimeWeaving` 是 Spring 框架提供的注解,用于启用加载时编织(Load Time Weaving,LTW),允许在类加载过程中动态织入切面逻辑,以实现诸如日志记录、性能监控等横切关注点的功能。 + +### 三、主要功能 + +1. **启用加载时编织(LTW)** + + + 允许在类加载的过程中动态地织入切面逻辑,而无需修改源代码或者使用特定的编译器。 + +2. **支持横切关注点的实现** + + + 通过加载时编织,可以将横切关注点与应用的核心业务逻辑分离,提高代码的模块化和可维护性。 + +3. **灵活性和可配置性** + + + 可以通过 AspectJ 提供的丰富语法和功能,灵活地定义切点和切面逻辑,以满足各种复杂的应用场景。 + +4. **不依赖源代码** + + + 加载时编织不依赖于源代码的修改或特殊的编译器,因此可以在已有的应用中轻松地引入切面逻辑,而无需对现有代码进行重构。 + +### 四、注解源码 + +注解 `@EnableLoadTimeWeaving`,用于激活 Spring 应用上下文中的加载时编织(Load Time Weaving)。通过该注解,可以方便地配置加载时编织,类似于 Spring XML 配置中的 `` 元素。同时,还可以通过 `aspectjWeaving()` 属性控制是否启用基于 AspectJ 的编织,提供了灵活的配置选项。 + +```java +/** + * 激活一个 Spring {@link LoadTimeWeaver} 用于该应用程序上下文,可作为一个名为 "loadTimeWeaver" 的 bean 使用, + * 类似于 Spring XML 中的 {@code } 元素。 + * + *

要在 @{@link org.springframework.context.annotation.Configuration Configuration} 类上使用; + * 最简单的示例如下 + * + *

+ * @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}。 + * + *

要定制使用的 weaver,{@code @Configuration} 类可以实现 {@link LoadTimeWeavingConfigurer} 接口,并通过 + * {@code #getLoadTimeWeaver} 方法返回一个自定义的 {@code LoadTimeWeaver} 实例 + * + *

+ * @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 } 的功能将自动启用。在使用 {@code @EnableLoadTimeWeaving(aspectjWeaving=ENABLED)} + * 时,这种情况不会发生。相反,您必须显式添加 {@code @EnableSpringConfigured}(包含在 {@code spring-aspects} 模块中)。 + * + * @author Chris Beams + * @since 3.1 + * @see LoadTimeWeaver + * @see DefaultContextLoadTimeWeaver + * @see org.aspectj.weaver.loadtime.ClassPreProcessorAgentAdapter + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Import(LoadTimeWeavingConfiguration.class) +public @interface EnableLoadTimeWeaving { + + /** + * 是否启用 AspectJ 编织。 + */ + AspectJWeaving aspectjWeaving() default AspectJWeaving.AUTODETECT; + + /** + * AspectJ 编织启用选项。 + */ + enum AspectJWeaving { + + /** + * 启用基于 Spring 的 AspectJ 加载时编织。 + */ + ENABLED, + + /** + * 关闭基于 Spring 的 AspectJ 加载时编织(即使类路径上存在 "META-INF/aop.xml" 资源)。 + */ + DISABLED, + + /** + * 如果类路径上存在 "META-INF/aop.xml" 资源,则启用 AspectJ 加载时编织。 + * 如果没有此类资源,则关闭 AspectJ 加载时编织。 + */ + AUTODETECT; + } + +} +``` + +### 五、最佳实践 + +使用加载时编织(Load Time Weaving)功能。首先,它创建了一个基于注解的 Spring 应用程序上下文,并通过 `AppConfig` 类配置了应用程序的相关组件。然后,它从应用程序上下文中获取了一个 `FooService` 的 bean 实例,并调用了其 `foo` 方法。接着,它创建了一个 `FooService` 的普通实例,并再次调用了其 `foo` 方法。 + +```java +public class EnableLoadTimeWeavingDemo { + + public static void main(String[] args) { + // 创建一个基于注解的应用程序上下文 + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); + // 从上下文中获取 FooService 的 bean 实例 + FooService fooService = context.getBean(FooService.class); + // 调用 FooService 的 foo 方法 + fooService.foo(); + // 换行 + System.out.println(); + // 创建一个 FooService 的实例 + FooService fooService1 = new FooService(); + // 调用 FooService 实例的 foo 方法 + fooService1.foo(); + } +} +``` + +通过 `@Configuration` 注解表明这是一个配置类,而 `@EnableLoadTimeWeaving` 注解则启用了加载时编织功能。在配置类中,通过 `@Bean` 注解定义了一个名为 `fooService` 的 bean,返回一个 `FooService` 的实例。 + +```java +@Configuration +@EnableLoadTimeWeaving +public class AppConfig { + + @Bean + public FooService fooService(){ + return new FooService(); + } +} +``` + +定义了一个切面类 `MyLTWAspect`,用于实现加载时编织(Load Time Weaving)功能。通过 `@Aspect` 注解标记该类为一个切面,并在其中定义了一个环绕通知方法 `around`,用于在目标方法调用前后执行特定逻辑。在该方法中,首先输出了目标方法的名称,然后调用了原始方法,并输出了方法返回值。同时,通过 `@Pointcut` 注解定义了一个切点 `ltwPointcut()`,指定了需要被切入的目标方法,这里是 `com.xcs.spring.FooService` 类中的所有公共方法。 + +```java +@Aspect +public class MyLTWAspect { + + @Around("ltwPointcut()") + public Object around(ProceedingJoinPoint pjp) throws Throwable { + // 在方法调用之前执行的逻辑 + System.out.println("Method " + pjp.getSignature().getName() + " is called."); + // 调用原始方法 + Object result = pjp.proceed(); + // 在方法调用之后执行的逻辑 + System.out.println("Method " + pjp.getSignature().getName() + " returns " + result); + return result; + } + + @Pointcut("execution(public * com.xcs.spring.FooService.*(..))") + public void ltwPointcut(){} +} +``` + +定义了加载时编织(Load Time Weaving)的规则和切面配置。在 `` 元素中指定了仅对应用程序特定包中的类进行编织,这里是 `com.xcs.spring` 包及其子包下的所有类。然后,在 `` 元素中指定了要编织的切面,即 `com.xcs.spring.MyLTWAspect`。 + +```java + + + + + + + + + + + + + + +``` + + `FooService` 类定义了一个简单的方法 `foo()`。 + +```java +public class FooService { + + public String foo() { + System.out.println("foo"); + return "this is a foo method"; + } +} +``` + +运行结果,加载时编织成功地拦截了通过 Spring 容器获取的 `FooService` bean,以及直接使用 `new` 操作符创建的 `FooService` 对象。在每次调用 `foo()` 方法时,都会先打印方法被调用的消息,然后执行原始的方法逻辑(打印 "foo"),最后打印方法返回值,并返回给调用方。这证明切面 `MyLTWAspect` 中定义的逻辑在目标方法调用前后得到了执行,不论是从 Spring 容器中获取的 bean 还是直接创建的对象,都受到了拦截。 + +```java +Method foo is called. +foo +Method foo returns this is a foo method + +Method foo is called. +foo +Method foo returns this is a foo method +``` + +### 六、源码分析 + +`LoadTimeWeavingConfiguration` 类,负责注册一个 `LoadTimeWeaver` bean,用于启用加载时编织(Load-Time Weaving)功能。在应用中使用 `@EnableLoadTimeWeaving` 注解时,这个配置类会被自动导入。它通过检查 `EnableLoadTimeWeaving` 注解的属性来决定是否启用 AspectJ 编织功能,并根据配置创建相应的 `LoadTimeWeaver` 实例。如果用户提供了自定义的 `LoadTimeWeavingConfigurer` 实例,则会使用用户提供的实例;否则,会创建一个默认的 `DefaultContextLoadTimeWeaver` 实例作为 `LoadTimeWeaver`。根据 `EnableLoadTimeWeaving` 注解中的配置,决定是否启用 AspectJ 编织功能,并根据情况调用 `AspectJWeavingEnabler` 中的方法来实现编织。 + +```java +/** + * {@code @Configuration} 类,注册一个 {@link LoadTimeWeaver} bean。 + * + *

当使用 {@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 @@ + + + + com.xcs.spring + spring-aop + 0.0.1-SNAPSHOT + + + jar + + 4.0.0 + spring-aop-enableLoadTimeWeaving + + + 1.9.7 + 2.3.5.RELEASE + + + + + org.springframework + spring-instrument + ${spring.version} + + + \ No newline at end of file diff --git a/spring-aop/spring-aop-enableLoadTimeWeaving/src/main/java/com/xcs/spring/AppConfig.java b/spring-aop/spring-aop-enableLoadTimeWeaving/src/main/java/com/xcs/spring/AppConfig.java new file mode 100644 index 0000000..701c2ec --- /dev/null +++ b/spring-aop/spring-aop-enableLoadTimeWeaving/src/main/java/com/xcs/spring/AppConfig.java @@ -0,0 +1,15 @@ +package com.xcs.spring; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.EnableLoadTimeWeaving; + +@Configuration +@EnableLoadTimeWeaving +public class AppConfig { + + @Bean + public FooService fooService(){ + return new FooService(); + } +} diff --git a/spring-aop/spring-aop-enableLoadTimeWeaving/src/main/java/com/xcs/spring/EnableLoadTimeWeavingDemo.java b/spring-aop/spring-aop-enableLoadTimeWeaving/src/main/java/com/xcs/spring/EnableLoadTimeWeavingDemo.java new file mode 100644 index 0000000..5c13556 --- /dev/null +++ b/spring-aop/spring-aop-enableLoadTimeWeaving/src/main/java/com/xcs/spring/EnableLoadTimeWeavingDemo.java @@ -0,0 +1,16 @@ +package com.xcs.spring; + +import org.springframework.context.annotation.AnnotationConfigApplicationContext; + +public class EnableLoadTimeWeavingDemo { + + public static void main(String[] args) { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); + FooService fooService = context.getBean(FooService.class); + fooService.foo(); + // 换行 + System.out.println(); + FooService fooService1 = new FooService(); + fooService1.foo(); + } +} diff --git a/spring-aop/spring-aop-enableLoadTimeWeaving/src/main/java/com/xcs/spring/FooService.java b/spring-aop/spring-aop-enableLoadTimeWeaving/src/main/java/com/xcs/spring/FooService.java new file mode 100644 index 0000000..43d2f09 --- /dev/null +++ b/spring-aop/spring-aop-enableLoadTimeWeaving/src/main/java/com/xcs/spring/FooService.java @@ -0,0 +1,10 @@ +package com.xcs.spring; + + +public class FooService { + + public String foo() { + System.out.println("foo"); + return "this is a foo method"; + } +} diff --git a/spring-aop/spring-aop-enableLoadTimeWeaving/src/main/java/com/xcs/spring/MyLTWAspect.java b/spring-aop/spring-aop-enableLoadTimeWeaving/src/main/java/com/xcs/spring/MyLTWAspect.java new file mode 100644 index 0000000..175b636 --- /dev/null +++ b/spring-aop/spring-aop-enableLoadTimeWeaving/src/main/java/com/xcs/spring/MyLTWAspect.java @@ -0,0 +1,24 @@ +package com.xcs.spring; + +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; + +@Aspect +public class MyLTWAspect { + + @Around("ltwPointcut()") + public Object around(ProceedingJoinPoint pjp) throws Throwable { + // 在方法调用之前执行的逻辑 + System.out.println("Method " + pjp.getSignature().getName() + " is called."); + // 调用原始方法 + Object result = pjp.proceed(); + // 在方法调用之后执行的逻辑 + System.out.println("Method " + pjp.getSignature().getName() + " returns " + result); + return result; + } + + @Pointcut("execution(public * com.xcs.spring.FooService.*(..))") + public void ltwPointcut(){} +} diff --git a/spring-aop/spring-aop-enableLoadTimeWeaving/src/main/resources/META-INF/aop.xml b/spring-aop/spring-aop-enableLoadTimeWeaving/src/main/resources/META-INF/aop.xml new file mode 100644 index 0000000..74b8e30 --- /dev/null +++ b/spring-aop/spring-aop-enableLoadTimeWeaving/src/main/resources/META-INF/aop.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file