From 9102e99d219e572327a8fb113e15f0370262cf92 Mon Sep 17 00:00:00 2001 From: linlei Date: Wed, 17 Apr 2024 14:14:44 +0800 Subject: [PATCH] =?UTF-8?q?AnnotationAwareAspectJAutoProxyCreator=E6=BA=90?= =?UTF-8?q?=E7=A0=81=E5=88=86=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- spring-aop/pom.xml | 8 - .../README.md | 587 ++++++++++++++++++ ...ationAwareAspectJAutoProxyCreatorDemo.java | 24 + .../main/java/com/xcs/spring/AppConfig.java | 2 - .../main/java/com/xcs/spring/MyAspect.java | 8 +- .../main/java/com/xcs/spring/MyService.java | 0 spring-aop/spring-aop-aspectj-after/pom.xml | 14 - .../spring-aop-aspectj-afterReturning/pom.xml | 14 - .../spring-aop-aspectj-afterThrowing/pom.xml | 14 - spring-aop/spring-aop-aspectj-around/pom.xml | 14 - .../spring-aop-aspectj-aspect/README.md | 31 - spring-aop/spring-aop-aspectj-aspect/pom.xml | 14 - .../main/java/com/xcs/spring/AspectDemo.java | 16 - .../java/com/xcs/spring/MyTestService.java | 11 - spring-aop/spring-aop-aspectj-before/pom.xml | 14 - .../spring-aop-aspectj-declareParents/pom.xml | 14 - .../spring-aop-aspectj-pointcut/pom.xml | 14 - 18 files changed, 619 insertions(+), 182 deletions(-) create mode 100644 spring-aop/spring-aop-annotationAwareAspectJAutoProxyCreator/README.md create mode 100644 spring-aop/spring-aop-annotationAwareAspectJAutoProxyCreator/src/main/java/com/xcs/spring/AnnotationAwareAspectJAutoProxyCreatorDemo.java rename spring-aop/{spring-aop-aspectj-aspect => spring-aop-annotationAwareAspectJAutoProxyCreator}/src/main/java/com/xcs/spring/AppConfig.java (70%) rename spring-aop/{spring-aop-aspectj-aspect => spring-aop-annotationAwareAspectJAutoProxyCreator}/src/main/java/com/xcs/spring/MyAspect.java (59%) rename spring-aop/{spring-aop-aspectj-aspect => spring-aop-annotationAwareAspectJAutoProxyCreator}/src/main/java/com/xcs/spring/MyService.java (100%) delete mode 100644 spring-aop/spring-aop-aspectj-after/pom.xml delete mode 100644 spring-aop/spring-aop-aspectj-afterReturning/pom.xml delete mode 100644 spring-aop/spring-aop-aspectj-afterThrowing/pom.xml delete mode 100644 spring-aop/spring-aop-aspectj-around/pom.xml delete mode 100644 spring-aop/spring-aop-aspectj-aspect/README.md delete mode 100644 spring-aop/spring-aop-aspectj-aspect/pom.xml delete mode 100644 spring-aop/spring-aop-aspectj-aspect/src/main/java/com/xcs/spring/AspectDemo.java delete mode 100644 spring-aop/spring-aop-aspectj-aspect/src/main/java/com/xcs/spring/MyTestService.java delete mode 100644 spring-aop/spring-aop-aspectj-before/pom.xml delete mode 100644 spring-aop/spring-aop-aspectj-declareParents/pom.xml delete mode 100644 spring-aop/spring-aop-aspectj-pointcut/pom.xml diff --git a/README.md b/README.md index ffa4b01..c59370a 100644 --- a/README.md +++ b/README.md @@ -218,7 +218,7 @@ - [AspectJAdvisorFactory](spring-aop/spring-aop-aspectJAdvisorFactory/README.md):创建AspectJ通知器实例,管理切面通知的创建和配置。 - [BeanFactoryAspectJAdvisorsBuilder](spring-aop/spring-aop-beanFactoryAspectJAdvisorsBuilder/README.md):构建@AspectJ注解切面,生成Spring AOP Advisors。 - [BeanFactoryAdvisorRetrievalHelper](spring-aop/spring-aop-beanFactoryAdvisorRetrievalHelper/README.md):帮助检索并管理 Spring AOP 中的 Advisor Beans。 - + - [AnnotationAwareAspectJAutoProxyCreator](spring-aop/spring-aop-annotationAwareAspectJAutoProxyCreator/README.md):创建AOP代理以应用AspectJ风格的切面。 + Spring AOT diff --git a/spring-aop/pom.xml b/spring-aop/pom.xml index 0a2ba83..a0c6085 100644 --- a/spring-aop/pom.xml +++ b/spring-aop/pom.xml @@ -18,14 +18,6 @@ spring-aop-methodMatcher spring-aop-jdkProxy spring-aop-cglibProxy - 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 diff --git a/spring-aop/spring-aop-annotationAwareAspectJAutoProxyCreator/README.md b/spring-aop/spring-aop-annotationAwareAspectJAutoProxyCreator/README.md new file mode 100644 index 0000000..0c8f0c3 --- /dev/null +++ b/spring-aop/spring-aop-annotationAwareAspectJAutoProxyCreator/README.md @@ -0,0 +1,587 @@ +## AnnotationAwareAspectJAutoProxyCreator + +- [AnnotationAwareAspectJAutoProxyCreator](#AnnotationAwareAspectJAutoProxyCreator) + - [一、基本信息](#一基本信息) + - [二、基本描述](#二基本描述) + - [三、主要功能](#三主要功能) + - [四、接口源码](#四接口源码) + - [五、主要实现](#五主要实现) + - [六、最佳实践](#六最佳实践) + - [七、源码分析](#七源码分析) + - [八、常见问题](#八常见问题) + +### 一、基本信息 + +✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) + +### 二、基本描述 + +`AnnotationAwareAspectJAutoProxyCreator`是Spring AOP中的关键类,负责自动检测标记有`@Aspect`注解的切面类,并将其代理到Spring应用程序的bean中,实现切面逻辑的自动织入,从而支持注解驱动的面向切面编程。 + +### 三、主要功能 + +1. **自动代理创建** + + + 检测应用程序上下文中被`@Aspect`注解标记的切面类,并自动创建代理对象,使切面逻辑能够在目标bean的方法调用中被织入。 + +2. **切面逻辑织入** + + + 将切面逻辑织入到目标bean的方法调用中,实现例如在方法执行前后、异常抛出时等的切面操作,以实现各种横切关注点的功能。 + +3. **支持注解驱动的切面编程** + + + 提供了基于注解的切面编程的支持,通过`@Aspect`等注解,开发者能够更便捷地定义切面类和切面逻辑。 + +4. **灵活的切面配置** + + + 允许我们通过注解方式配置切面,而不需要在XML配置文件中显式声明,从而使切面的配置更加灵活和便捷。 + +5. **AOP功能整合** + + + 将AspectJ切面功能整合到Spring框架中,使得我们能够在Spring应用程序中使用AspectJ风格的切面编程,从而更好地实现横切关注点的模块化和重用。 + +### 四、类关系图 + +~~~mermaid +classDiagram +direction BT +class AbstractAdvisorAutoProxyCreator +class AbstractAutoProxyCreator +class AnnotationAwareAspectJAutoProxyCreator +class AopInfrastructureBean { +<> + +} +class AspectJAwareAdvisorAutoProxyCreator +class Aware { +<> + +} +class BeanClassLoaderAware { +<> + +} +class BeanFactoryAware { +<> + +} +class BeanPostProcessor { +<> + +} +class InstantiationAwareBeanPostProcessor { +<> + +} +class Ordered { +<> + +} +class ProxyConfig +class ProxyProcessorSupport +class Serializable { +<> + +} +class SmartInstantiationAwareBeanPostProcessor { +<> + +} + +AbstractAdvisorAutoProxyCreator --> AbstractAutoProxyCreator +AbstractAutoProxyCreator ..> BeanFactoryAware +AbstractAutoProxyCreator --> ProxyProcessorSupport +AbstractAutoProxyCreator ..> SmartInstantiationAwareBeanPostProcessor +AnnotationAwareAspectJAutoProxyCreator --> AspectJAwareAdvisorAutoProxyCreator +AspectJAwareAdvisorAutoProxyCreator --> AbstractAdvisorAutoProxyCreator +BeanClassLoaderAware --> Aware +BeanFactoryAware --> Aware +InstantiationAwareBeanPostProcessor --> BeanPostProcessor +ProxyConfig ..> Serializable +ProxyProcessorSupport ..> AopInfrastructureBean +ProxyProcessorSupport ..> BeanClassLoaderAware +ProxyProcessorSupport ..> Ordered +ProxyProcessorSupport --> ProxyConfig +SmartInstantiationAwareBeanPostProcessor --> InstantiationAwareBeanPostProcessor +~~~ + +### 五、最佳实践 + +`AnnotationAwareAspectJAutoProxyCreator`类以及注解驱动的切面编程。通过注册`AnnotationAwareAspectJAutoProxyCreator`作为Bean,并指定应用程序配置类,Spring容器能够自动创建切面代理,并在运行时织入切面逻辑。最后,从容器中获取`MyService` bean并调用其方法,实现了面向切面编程的横切关注点的功能。 + +```java +public class AnnotationAwareAspectJAutoProxyCreatorDemo { + + public static void main(String[] args) { + // 创建一个默认的 Bean 工厂 + AnnotationConfigApplicationContext beanFactory = new AnnotationConfigApplicationContext(); + // 注册AnnotationAwareAspectJAutoProxyCreator作为Bean,用于自动创建切面代理 + beanFactory.registerBeanDefinition("internalAutoProxyCreator", new RootBeanDefinition(AnnotationAwareAspectJAutoProxyCreator.class)); + // 注册应用程序配置类 + beanFactory.register(AppConfig.class); + // 刷新应用程序上下文 + beanFactory.refresh(); + + // 从容器中获取MyService bean + MyService myService = beanFactory.getBean(MyService.class); + // 调用MyService的方法 + myService.doSomething(); + } +} +``` + +运行结果,执行`MyService`中的`doSomething()`方法之前和之后,切面逻辑已成功织入。 + +```java +Before executing the method... +Doing something... +After executing the method... +``` + +### 六、时序图 + +~~~mermaid +sequenceDiagram +autonumber +BeanFactory->>AbstractAutoProxyCreator:postProcessAfterInitialization() +Note over BeanFactory,AbstractAutoProxyCreator: BeanFactory调用初始化后处理方法 +AbstractAutoProxyCreator->>AbstractAutoProxyCreator:wrapIfNecessary() +Note over AbstractAutoProxyCreator,AbstractAutoProxyCreator: 条件判断与代理创建 +AbstractAutoProxyCreator->>AbstractAdvisorAutoProxyCreator:getAdvicesAndAdvisorsForBean() +Note over AbstractAutoProxyCreator,AbstractAdvisorAutoProxyCreator: 获取并返回适用的Advisors数组 +AbstractAdvisorAutoProxyCreator->>AbstractAdvisorAutoProxyCreator:findEligibleAdvisors() +Note over AbstractAdvisorAutoProxyCreator,AbstractAdvisorAutoProxyCreator: 查找并扩展可应用的Advisors列表 +AbstractAdvisorAutoProxyCreator->>AnnotationAwareAspectJAutoProxyCreator:findCandidateAdvisors() +Note over AbstractAdvisorAutoProxyCreator,AnnotationAwareAspectJAutoProxyCreator: 添加Spring和AspectJ,合并为候选Advisors列表 +AnnotationAwareAspectJAutoProxyCreator->>AbstractAdvisorAutoProxyCreator:super.findCandidateAdvisors() +Note over AnnotationAwareAspectJAutoProxyCreator,AbstractAdvisorAutoProxyCreator: 获取自动代理的候选Advisors列表 +AbstractAdvisorAutoProxyCreator->>BeanFactoryAdvisorRetrievalHelper:findAdvisorBeans() +Note over AbstractAdvisorAutoProxyCreator,BeanFactoryAdvisorRetrievalHelper: 获取当前bean工厂中所有合格的Advisor beans +AnnotationAwareAspectJAutoProxyCreator->>BeanFactoryAspectJAdvisorsBuilder:buildAspectJAdvisors() +Note over AnnotationAwareAspectJAutoProxyCreator,BeanFactoryAspectJAdvisorsBuilder: 获取AspectJ注解的切面并创建Advisor +AnnotationAwareAspectJAutoProxyCreator->>AbstractAdvisorAutoProxyCreator:返回Advisors +AbstractAdvisorAutoProxyCreator->>AbstractAdvisorAutoProxyCreator:findAdvisorsThatCanApply() +Note over AbstractAdvisorAutoProxyCreator,AbstractAdvisorAutoProxyCreator: 查找适用的候选顾问并设置代理 +AbstractAdvisorAutoProxyCreator->>AopUtils:findAdvisorsThatCanApply() +Note over AbstractAdvisorAutoProxyCreator,AopUtils: 筛选适用的顾问并添加到列表。 +AopUtils->>AopUtils:canApply(advisor,targetClass,hasIntroductions) +Note over AopUtils,AopUtils: 判断顾问是否适用于目标类。 +AopUtils->>AopUtils:canApply(pc,targetClass,hasIntroductions) +Note over AopUtils,AopUtils: 判断切点是否适用于目标类。 +AbstractAdvisorAutoProxyCreator->>AspectJAwareAdvisorAutoProxyCreator:extendAdvisors() +Note over AbstractAdvisorAutoProxyCreator,AspectJAwareAdvisorAutoProxyCreator: 添加AspectJ支持到Advisor链。 +AspectJAwareAdvisorAutoProxyCreator->>AspectJProxyUtils:makeAdvisorChainAspectJCapableIfNecessary() +Note over AspectJAwareAdvisorAutoProxyCreator,AspectJProxyUtils: 在Advisor链中添加AspectJ支持。 +AbstractAdvisorAutoProxyCreator->>AbstractAutoProxyCreator:返回拦截器 +AbstractAutoProxyCreator->>AbstractAutoProxyCreator:createProxy() +Note over AbstractAutoProxyCreator,AbstractAutoProxyCreator: 创建 AOP 代理对象的过程。 +AbstractAutoProxyCreator->>ProxyFactory:new ProxyFactory() +ProxyFactory->>AbstractAutoProxyCreator:返回proxyFactory +AbstractAutoProxyCreator->>ProxyFactory:getProxy() +ProxyFactory->>AbstractAutoProxyCreator:返回代理对象 +AbstractAutoProxyCreator->>BeanFactory:返回代理对象 +~~~ + +### 七、源码分析 + +在`org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#postProcessAfterInitialization`方法中,用于在初始化后对bean进行后置处理。它的作用是检查是否需要对bean创建代理,并在需要时使用配置的拦截器创建代理对象,以实现AOP功能。 + +```java +/** + * 如果子类确定要将该bean标识为需要代理的bean,则使用配置的拦截器创建代理。 + * @see #getAdvicesAndAdvisorsForBean + */ +@Override +public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) { + if (bean != null) { + // 获取缓存键 + Object cacheKey = getCacheKey(bean.getClass(), beanName); + // 如果早期代理引用集合中存在该bean,并且不是同一引用,则进行包装 + if (this.earlyProxyReferences.remove(cacheKey) != bean) { + return wrapIfNecessary(bean, beanName, cacheKey); + } + } + // 返回bean + return bean; +} +``` + +在`org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#wrapIfNecessary`方法中,首先检查bean的名称是否存在且是否属于目标源bean,如果是,则直接返回原始bean实例;然后检查缓存中是否存在已标记为不需要代理的bean,如果是,则同样直接返回原始bean实例;接着检查bean的类是否为基础结构类或者是否应该跳过该bean,如果是,则将其标记为不需要代理并返回原始bean实例;最后,如果存在通知(拦截器),则创建代理对象并返回,否则同样将其标记为不需要代理并返回原始bean实例。 + +```java +/** + * 如果需要,对给定的bean进行包装,即如果它符合被代理的条件。 + * @param bean 原始的bean实例 + * @param beanName bean的名称 + * @param cacheKey 元数据访问的缓存键 + * @return 包装了bean的代理,或者原始的bean实例 + */ +protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) { + // 如果beanName非空,并且该bean已经被目标源bean包含,则直接返回原始的bean实例 + if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) { + return bean; + } + // 如果缓存中已经存在该bean的标记为不需要代理,则直接返回原始的bean实例 + if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) { + return bean; + } + // 如果bean的类为基础结构类,或者应该跳过该bean的类,则直接返回原始的bean实例 + if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) { + this.advisedBeans.put(cacheKey, Boolean.FALSE); + return bean; + } + + // 如果存在通知,则创建代理 + Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null); + if (specificInterceptors != DO_NOT_PROXY) { + this.advisedBeans.put(cacheKey, Boolean.TRUE); + Object proxy = createProxy( + bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean)); + this.proxyTypes.put(cacheKey, proxy.getClass()); + return proxy; + } + + // 没有通知,将bean标记为不需要代理 + this.advisedBeans.put(cacheKey, Boolean.FALSE); + return bean; +} +``` + +在`org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator#getAdvicesAndAdvisorsForBean`方法中,首先调用`findEligibleAdvisors`方法查找适用于该bean的Advisors,然后将其转换为数组并返回。如果没有找到适用的Advisors,则返回一个特定的标记值`DO_NOT_PROXY`。 + +```java +/** + * 获取适用于给定bean的Advisors。 + * @param beanClass bean的类 + * @param beanName bean的名称 + * @param targetSource 目标源 + * @return 包含Advisors的数组,如果没有找到适用的Advisors,则返回DO_NOT_PROXY + */ +@Override +@Nullable +protected Object[] getAdvicesAndAdvisorsForBean( + Class beanClass, String beanName, @Nullable TargetSource targetSource) { + + // 查找适用于bean的Advisors + List advisors = findEligibleAdvisors(beanClass, beanName); + // 如果没有找到适用的Advisors,则返回DO_NOT_PROXY + if (advisors.isEmpty()) { + return DO_NOT_PROXY; + } + // 返回Advisors的数组 + return advisors.toArray(); +} +``` + +在`org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator#findEligibleAdvisors`方法中,主要用于查找适用于自动代理给定类的所有合适的 Advisors。它首先调用 findCandidateAdvisors 方法来查找候选的 Advisors,然后通过 findAdvisorsThatCanApply 方法筛选出可以应用于当前类的 Advisors。接着,它调用 extendAdvisors 方法来扩展 Advisors 列表,以确保适当的拦截器和通知已被应用。最后,如果有适用的 Advisors,则对 Advisors 列表进行排序并返回;如果没有适用的 Advisors,则返回一个空列表。 + +```java +/** + * 查找适用于自动代理该类的所有合格Advisors。 + * @param beanClass 要查找Advisors的类 + * @param beanName 当前代理的bean的名称 + * @return 空列表,非null,如果没有切点或拦截器 + * @see #findCandidateAdvisors + * @see #sortAdvisors + * @see #extendAdvisors + */ +protected List findEligibleAdvisors(Class beanClass, String beanName) { + // 查找候选Advisors + List candidateAdvisors = findCandidateAdvisors(); + // 查找可应用的Advisors + List eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName); + // 扩展Advisors + extendAdvisors(eligibleAdvisors); + // 如果有可应用的Advisors,则对Advisors进行排序 + if (!eligibleAdvisors.isEmpty()) { + eligibleAdvisors = sortAdvisors(eligibleAdvisors); + } + return eligibleAdvisors; +} +``` + +在`org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator#findCandidateAdvisors`方法中,重写了父类方法 `findCandidateAdvisors()`,首先调用了父类方法以获取所有Spring Advisors,然后利用 `aspectJAdvisorsBuilder` 构建了所有AspectJ切面对应的Advisors,并将其添加到`advisors`列表中返回。 + +[BeanFactoryAspectJAdvisorsBuilder源码分析](./spring-aop-beanFactoryAspectJAdvisorsBuilder/README.md) + +```java +@Override +protected List findCandidateAdvisors() { + // 添加根据超类规则找到的所有Spring顾问。 + List advisors = super.findCandidateAdvisors(); + // 为bean工厂中的所有AspectJ切面构建顾问。 + if (this.aspectJAdvisorsBuilder != null) { + advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors()); + } + return advisors; +} +``` + +在`org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator#findCandidateAdvisors`方法中,调用`BeanFactoryAdvisorRetrievalHelper.findAdvisorBeans()` 方法来获取候选的Advisors列表。 + +[BeanFactoryAdvisorRetrievalHelper源码分析](./spring-aop-beanFactoryAdvisorRetrievalHelper/README.md) + +```java +/** + * 查找用于自动代理的所有候选Advisors。 + * @return 候选Advisors的列表 + */ +protected List findCandidateAdvisors() { + Assert.state(this.advisorRetrievalHelper != null, "No BeanFactoryAdvisorRetrievalHelper available"); + return this.advisorRetrievalHelper.findAdvisorBeans(); +} +``` + +在`org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator#findAdvisorsThatCanApply`方法中,搜索给定的候选 Advisors,以找到所有能够应用于指定 bean 的 Advisors。它设置当前被代理的 bean 名称,并尝试查找所有适用于指定 bean 的 Advisors。在查找完成后,它会清除当前被代理的 bean 名称。 + +```java +/** + * 搜索给定的候选顾问,找到所有适用于指定bean的顾问。 + * @param candidateAdvisors 候选顾问列表 + * @param beanClass 目标bean的类 + * @param beanName 目标bean的名称 + * @return 适用的顾问列表 + * @see ProxyCreationContext#getCurrentProxiedBeanName() + */ +protected List findAdvisorsThatCanApply( + List candidateAdvisors, Class beanClass, String beanName) { + + // 设置当前代理的bean名称 + ProxyCreationContext.setCurrentProxiedBeanName(beanName); + try { + // 查找适用于指定bean的顾问 + return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass); + } + finally { + // 清除当前代理的bean名称 + ProxyCreationContext.setCurrentProxiedBeanName(null); + } +} +``` + +在`org.springframework.aop.support.AopUtils#findAdvisorsThatCanApply`方法中,用于确定适用于给定类的候选Advisors子列表。它遍历给定的Advisors列表,首先将能够应用于指定类的引介Advisors添加到结果列表中。然后,它检查是否已经存在引介Advisors,如果存在,则跳过;如果不存在,则继续遍历Advisors列表,并将能够应用于指定类的其他Advisors添加到结果列表中。最终返回这个子列表,其中包含可以应用于给定类的所有Advisors。 + +```java +/** + * 确定 {@code candidateAdvisors} 列表中适用于给定类的子列表。 + * @param candidateAdvisors 要评估的顾问列表 + * @param clazz 目标类 + * @return 可应用于给定类的顾问子列表 + * (可能是原始列表) + */ +public static List findAdvisorsThatCanApply(List candidateAdvisors, Class clazz) { + // 如果候选顾问列表为空,则直接返回空列表 + if (candidateAdvisors.isEmpty()) { + return candidateAdvisors; + } + // 创建一个用于存储适用于给定类的顾问的列表 + List eligibleAdvisors = new ArrayList<>(); + // 遍历候选顾问列表 + for (Advisor candidate : candidateAdvisors) { + // 如果候选顾问是引介顾问,并且可以应用于给定类,则将其添加到结果列表中 + if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) { + eligibleAdvisors.add(candidate); + } + } + // 检查是否存在引介顾问 + boolean hasIntroductions = !eligibleAdvisors.isEmpty(); + // 继续遍历候选顾问列表 + for (Advisor candidate : candidateAdvisors) { + // 如果候选顾问是引介顾问,则跳过 + if (candidate instanceof IntroductionAdvisor) { + // 已经处理过 + continue; + } + // 如果候选顾问可以应用于给定类,则将其添加到结果列表中 + if (canApply(candidate, clazz, hasIntroductions)) { + eligibleAdvisors.add(candidate); + } + } + return eligibleAdvisors; +} +``` + +在`org.springframework.aop.support.AopUtils#canApply(org.springframework.aop.Advisor, java.lang.Class, boolean)`方法中,判断给定的顾问是否能够在指定的类上应用。它首先检查顾问是否是引介顾问,如果是,则通过类过滤器来判断是否可以应用于目标类。如果顾问不是引介顾问,而是切点顾问,则通过切点来判断是否可以应用于目标类。如果顾问既不是引介顾问也不是切点顾问,则假设它适用于目标类。 + +```java +/** + * 判断给定的顾问是否能够在指定的类上应用。 + *

这是一个重要的测试,因为它可以用于优化掉一个类的顾问。 + * 这个版本还考虑了引介(对于IntroductionAwareMethodMatchers)。 + * @param advisor 要检查的顾问 + * @param targetClass 我们正在测试的类 + * @param hasIntroductions 顾问链中是否包含任何引介 + * @return 切点是否能够应用于任何方法 + */ +public static boolean canApply(Advisor advisor, Class targetClass, boolean hasIntroductions) { + // 如果顾问是引介顾问,则通过类过滤器来判断是否可以应用于目标类 + if (advisor instanceof IntroductionAdvisor) { + return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass); + } + // 如果顾问是切点顾问,则通过切点来判断是否可以应用于目标类 + else if (advisor instanceof PointcutAdvisor) { + PointcutAdvisor pca = (PointcutAdvisor) advisor; + return canApply(pca.getPointcut(), targetClass, hasIntroductions); + } + // 否则,假设它适用于目标类 + else { + // 它没有切点,因此我们假设它适用。 + return true; + } +} +``` + +在`org.springframework.aop.support.AopUtils#canApply(org.springframework.aop.Pointcut, java.lang.Class, boolean)`方法中,确定给定的切点是否能够在指定的类上应用。首先,它检查切点的类过滤器是否与目标类匹配。如果不匹配,则返回 false。如果类过滤器匹配目标类,它会检查方法匹配器是否为 MethodMatcher.TRUE,如果是,则表示切点适用于目标类的任何方法,直接返回 true。如果方法匹配器不是 MethodMatcher.TRUE,则遍历目标类及其所有接口,并检查每个类中的方法是否与切点匹配。如果找到匹配的方法,则返回 true;如果没有找到匹配的方法,则返回 false。 + +```java +/** + * 判断给定的切点是否能够在指定的类上应用。 + *

这是一个重要的测试,因为它可以用于优化掉一个类的切点。 + * @param pc 要检查的静态或动态切点 + * @param targetClass 要测试的类 + * @param hasIntroductions 顾问链中是否包含任何引介 + * @return 切点是否能够应用于任何方法 + */ +public static boolean canApply(Pointcut pc, Class targetClass, boolean hasIntroductions) { + Assert.notNull(pc, "切点不能为空"); + // 首先检查类过滤器是否匹配目标类 + if (!pc.getClassFilter().matches(targetClass)) { + return false; + } + + MethodMatcher methodMatcher = pc.getMethodMatcher(); + // 如果方法匹配器是 MethodMatcher.TRUE,则不需要遍历方法,直接返回true + if (methodMatcher == MethodMatcher.TRUE) { + // 如果我们匹配任何方法,则不需要遍历方法... + return true; + } + + IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null; + if (methodMatcher instanceof IntroductionAwareMethodMatcher) { + introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher; + } + + // 获取目标类及其所有接口的集合 + Set> classes = new LinkedHashSet<>(); + // 如果目标类不是代理类,则将其添加到类集合中 + if (!Proxy.isProxyClass(targetClass)) { + classes.add(ClassUtils.getUserClass(targetClass)); + } + // 将目标类的所有接口添加到类集合中 + classes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetClass)); + + // 遍历类集合 + for (Class clazz : classes) { + // 获取类中声明的所有方法 + Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz); + // 遍历方法 + for (Method method : methods) { + // 如果存在引介感知的方法匹配器,并且方法匹配,则返回true; + // 否则,如果方法匹配器匹配方法,则返回true + if (introductionAwareMethodMatcher != null ? + introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) : + methodMatcher.matches(method, targetClass)) { + return true; + } + } + } + + return false; +} +``` + +在`org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator#extendAdvisors`方法中,将 `ExposeInvocationInterceptor` 添加到通知链的开头。这是必要的额外处理,特别是在使用 AspectJ 切点表达式和 AspectJ 风格的建议时。 + +```java +/** + * 将 {@link ExposeInvocationInterceptor} 添加到通知链的开头。 + *

在使用AspectJ切点表达式和AspectJ风格的建议时,需要添加此额外的建议。 + * @Override + * @param candidateAdvisors 候选的Advisors列表 + */ +@Override +protected void extendAdvisors(List candidateAdvisors) { + // 如果需要,使Advisor链支持AspectJ + AspectJProxyUtils.makeAdvisorChainAspectJCapableIfNecessary(candidateAdvisors); +} +``` + +在`org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#createProxy`方法中,首先检查`BeanFactory`是否是`ConfigurableListableBeanFactory`类型的,如果是,则调用`AutoProxyUtils.exposeTargetClass`方法来暴露目标类。然后创建一个`ProxyFactory`实例,并根据当前的代理配置进行设置。根据是否启用了代理目标类的标志,决定是否将代理目标类设置为true或false。接着构建适用于该bean的所有顾问,将它们添加到`ProxyFactory`中,并设置目标源为预先配置好的`TargetSource`。最后,根据当前的代理配置和代理类加载器,使用`ProxyFactory`获取代理实例并返回。 + +```java +/** + * 为给定的 bean 创建一个 AOP 代理。 + * @param beanClass bean 的类 + * @param beanName bean 的名称 + * @param specificInterceptors 适用于此 bean 的拦截器集合(可能为空,但不为 null) + * @param targetSource 代理的 TargetSource,已预先配置以访问该 bean + * @return bean 的 AOP 代理 + * @see #buildAdvisors + */ +protected Object createProxy(Class beanClass, @Nullable String beanName, + @Nullable Object[] specificInterceptors, TargetSource targetSource) { + + // 如果 beanFactory 是 ConfigurableListableBeanFactory 类型的,则暴露目标类 + if (this.beanFactory instanceof ConfigurableListableBeanFactory) { + AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass); + } + + // 创建 ProxyFactory 实例 + ProxyFactory proxyFactory = new ProxyFactory(); + proxyFactory.copyFrom(this); + + // 如果需要使用代理目标类,则设置为 true + if (proxyFactory.isProxyTargetClass()) { + // 对 JDK 代理目标进行显式处理(用于介绍建议场景) + if (Proxy.isProxyClass(beanClass)) { + // 必须允许引入;不能只设置接口为代理的接口。 + for (Class ifc : beanClass.getInterfaces()) { + proxyFactory.addInterface(ifc); + } + } + } else { + // 未强制代理目标类标志,让我们应用默认检查... + if (shouldProxyTargetClass(beanClass, beanName)) { + proxyFactory.setProxyTargetClass(true); + } else { + evaluateProxyInterfaces(beanClass, proxyFactory); + } + } + + // 构建顾问数组 + Advisor[] advisors = buildAdvisors(beanName, specificInterceptors); + // 将顾问添加到 ProxyFactory + proxyFactory.addAdvisors(advisors); + proxyFactory.setTargetSource(targetSource); + // 自定义 ProxyFactory + customizeProxyFactory(proxyFactory); + + proxyFactory.setFrozen(this.freezeProxy); + if (advisorsPreFiltered()) { + proxyFactory.setPreFiltered(true); + } + + // 如果 bean 类没有在重写类加载器中本地加载,则使用原始 ClassLoader + ClassLoader classLoader = getProxyClassLoader(); + if (classLoader instanceof SmartClassLoader && classLoader != beanClass.getClassLoader()) { + classLoader = ((SmartClassLoader) classLoader).getOriginalClassLoader(); + } + // 获取代理实例 + return proxyFactory.getProxy(classLoader); +} +``` + +### 八、常见问题 + +1. **无法注入依赖** + + + 可能会遇到无法注入依赖的问题,这通常是由于AOP代理的顺序问题导致的。 + +2. **切面未生效** + + + 有时配置了切面,但是切面并没有生效,可能是由于切面表达式不正确,或者AOP代理配置错误导致的。 + + +4. **Bean未被代理** + + + 有时候配置了AnnotationAwareAspectJAutoProxyCreator,但是某些Bean并没有被代理,可能是由于Bean的作用域、类型或其他条件不满足自动代理的条件。 \ No newline at end of file diff --git a/spring-aop/spring-aop-annotationAwareAspectJAutoProxyCreator/src/main/java/com/xcs/spring/AnnotationAwareAspectJAutoProxyCreatorDemo.java b/spring-aop/spring-aop-annotationAwareAspectJAutoProxyCreator/src/main/java/com/xcs/spring/AnnotationAwareAspectJAutoProxyCreatorDemo.java new file mode 100644 index 0000000..2a5e3f3 --- /dev/null +++ b/spring-aop/spring-aop-annotationAwareAspectJAutoProxyCreator/src/main/java/com/xcs/spring/AnnotationAwareAspectJAutoProxyCreatorDemo.java @@ -0,0 +1,24 @@ +package com.xcs.spring; + +import org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator; +import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; + +public class AnnotationAwareAspectJAutoProxyCreatorDemo { + + public static void main(String[] args) { + // 创建一个默认的 Bean 工厂 + AnnotationConfigApplicationContext beanFactory = new AnnotationConfigApplicationContext(); + // 注册AnnotationAwareAspectJAutoProxyCreator作为Bean,用于自动创建切面代理 + beanFactory.registerBeanDefinition("internalAutoProxyCreator", new RootBeanDefinition(AnnotationAwareAspectJAutoProxyCreator.class)); + // 注册应用程序配置类 + beanFactory.register(AppConfig.class); + // 刷新应用程序上下文 + beanFactory.refresh(); + + // 从容器中获取MyService bean + MyService myService = beanFactory.getBean(MyService.class); + // 调用MyService的方法 + myService.doSomething(); + } +} diff --git a/spring-aop/spring-aop-aspectj-aspect/src/main/java/com/xcs/spring/AppConfig.java b/spring-aop/spring-aop-annotationAwareAspectJAutoProxyCreator/src/main/java/com/xcs/spring/AppConfig.java similarity index 70% rename from spring-aop/spring-aop-aspectj-aspect/src/main/java/com/xcs/spring/AppConfig.java rename to spring-aop/spring-aop-annotationAwareAspectJAutoProxyCreator/src/main/java/com/xcs/spring/AppConfig.java index a8a3b1d..b33612f 100644 --- a/spring-aop/spring-aop-aspectj-aspect/src/main/java/com/xcs/spring/AppConfig.java +++ b/spring-aop/spring-aop-annotationAwareAspectJAutoProxyCreator/src/main/java/com/xcs/spring/AppConfig.java @@ -2,10 +2,8 @@ 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("com.xcs.spring") public class AppConfig { } diff --git a/spring-aop/spring-aop-aspectj-aspect/src/main/java/com/xcs/spring/MyAspect.java b/spring-aop/spring-aop-annotationAwareAspectJAutoProxyCreator/src/main/java/com/xcs/spring/MyAspect.java similarity index 59% rename from spring-aop/spring-aop-aspectj-aspect/src/main/java/com/xcs/spring/MyAspect.java rename to spring-aop/spring-aop-annotationAwareAspectJAutoProxyCreator/src/main/java/com/xcs/spring/MyAspect.java index f2d8c7c..3baeade 100644 --- a/spring-aop/spring-aop-aspectj-aspect/src/main/java/com/xcs/spring/MyAspect.java +++ b/spring-aop/spring-aop-annotationAwareAspectJAutoProxyCreator/src/main/java/com/xcs/spring/MyAspect.java @@ -1,5 +1,6 @@ package com.xcs.spring; +import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; @@ -9,7 +10,12 @@ import org.springframework.stereotype.Component; class MyAspect { @Before("execution(* com.xcs.spring.MyService.doSomething(..))") - public void beforeAdvice() { + public void before() { System.out.println("Before executing the method..." ); } + + @After("execution(* com.xcs.spring.MyService.doSomething(..))") + public void after() { + System.out.println("After executing the method..." ); + } } diff --git a/spring-aop/spring-aop-aspectj-aspect/src/main/java/com/xcs/spring/MyService.java b/spring-aop/spring-aop-annotationAwareAspectJAutoProxyCreator/src/main/java/com/xcs/spring/MyService.java similarity index 100% rename from spring-aop/spring-aop-aspectj-aspect/src/main/java/com/xcs/spring/MyService.java rename to spring-aop/spring-aop-annotationAwareAspectJAutoProxyCreator/src/main/java/com/xcs/spring/MyService.java diff --git a/spring-aop/spring-aop-aspectj-after/pom.xml b/spring-aop/spring-aop-aspectj-after/pom.xml deleted file mode 100644 index 81ab7b8..0000000 --- a/spring-aop/spring-aop-aspectj-after/pom.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - com.xcs.spring - spring-aop - 0.0.1-SNAPSHOT - - - 4.0.0 - 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 deleted file mode 100644 index 41cabb8..0000000 --- a/spring-aop/spring-aop-aspectj-afterReturning/pom.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - 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 deleted file mode 100644 index 3da69cf..0000000 --- a/spring-aop/spring-aop-aspectj-afterThrowing/pom.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - 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-aspectj-around/pom.xml b/spring-aop/spring-aop-aspectj-around/pom.xml deleted file mode 100644 index 9eb5db2..0000000 --- a/spring-aop/spring-aop-aspectj-around/pom.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - com.xcs.spring - spring-aop - 0.0.1-SNAPSHOT - - - 4.0.0 - 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 deleted file mode 100644 index e6c562d..0000000 --- a/spring-aop/spring-aop-aspectj-aspect/README.md +++ /dev/null @@ -1,31 +0,0 @@ -## 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-aspectj-aspect/pom.xml b/spring-aop/spring-aop-aspectj-aspect/pom.xml deleted file mode 100644 index e1e8bb5..0000000 --- a/spring-aop/spring-aop-aspectj-aspect/pom.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - com.xcs.spring - spring-aop - 0.0.1-SNAPSHOT - - - 4.0.0 - 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/AspectDemo.java b/spring-aop/spring-aop-aspectj-aspect/src/main/java/com/xcs/spring/AspectDemo.java deleted file mode 100644 index b898878..0000000 --- a/spring-aop/spring-aop-aspectj-aspect/src/main/java/com/xcs/spring/AspectDemo.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.xcs.spring; - -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("MyService = " + service.getClass()); - service.doSomething(); - - MyTestService myTestService = context.getBean(MyTestService.class); - System.out.println("MyTestService = " + myTestService.getClass()); - myTestService.test(); - } -} diff --git a/spring-aop/spring-aop-aspectj-aspect/src/main/java/com/xcs/spring/MyTestService.java b/spring-aop/spring-aop-aspectj-aspect/src/main/java/com/xcs/spring/MyTestService.java deleted file mode 100644 index cf8a64f..0000000 --- a/spring-aop/spring-aop-aspectj-aspect/src/main/java/com/xcs/spring/MyTestService.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.xcs.spring; - -import org.springframework.stereotype.Service; - -@Service -public class MyTestService { - - public void test() { - System.out.println("test"); - } -} diff --git a/spring-aop/spring-aop-aspectj-before/pom.xml b/spring-aop/spring-aop-aspectj-before/pom.xml deleted file mode 100644 index 3bb391a..0000000 --- a/spring-aop/spring-aop-aspectj-before/pom.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - com.xcs.spring - spring-aop - 0.0.1-SNAPSHOT - - - 4.0.0 - 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 deleted file mode 100644 index c4f0e11..0000000 --- a/spring-aop/spring-aop-aspectj-declareParents/pom.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - 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 deleted file mode 100644 index cac648c..0000000 --- a/spring-aop/spring-aop-aspectj-pointcut/pom.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - com.xcs.spring - spring-aop - 0.0.1-SNAPSHOT - - - 4.0.0 - spring-aop-aspectj-pointcut - - \ No newline at end of file