From 963435db770ffe357937fa3a7bb68ea81c7fbcf6 Mon Sep 17 00:00:00 2001 From: linlei Date: Tue, 16 Apr 2024 14:43:26 +0800 Subject: [PATCH] =?UTF-8?q?BeanFactoryAdvisorRetrievalHelper=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 --- spring-aop/pom.xml | 1 + .../README.md | 162 ++++++++++++++++++ .../pom.xml | 14 ++ ...BeanFactoryAdvisorRetrievalHelperDemo.java | 24 +++ .../main/java/com/xcs/spring/MyAdvisor.java | 19 ++ .../README.md | 7 + 6 files changed, 227 insertions(+) create mode 100644 spring-aop/spring-aop-beanFactoryAdvisorRetrievalHelper/README.md create mode 100644 spring-aop/spring-aop-beanFactoryAdvisorRetrievalHelper/pom.xml create mode 100644 spring-aop/spring-aop-beanFactoryAdvisorRetrievalHelper/src/main/java/com/xcs/spring/BeanFactoryAdvisorRetrievalHelperDemo.java create mode 100644 spring-aop/spring-aop-beanFactoryAdvisorRetrievalHelper/src/main/java/com/xcs/spring/MyAdvisor.java diff --git a/spring-aop/pom.xml b/spring-aop/pom.xml index 02a6c1b..0a2ba83 100644 --- a/spring-aop/pom.xml +++ b/spring-aop/pom.xml @@ -39,6 +39,7 @@ spring-aop-metadataAwareAspectInstanceFactory spring-aop-aspectJAdvisorFactory spring-aop-beanFactoryAspectJAdvisorsBuilder + spring-aop-beanFactoryAdvisorRetrievalHelper 4.0.0 diff --git a/spring-aop/spring-aop-beanFactoryAdvisorRetrievalHelper/README.md b/spring-aop/spring-aop-beanFactoryAdvisorRetrievalHelper/README.md new file mode 100644 index 0000000..a887d9c --- /dev/null +++ b/spring-aop/spring-aop-beanFactoryAdvisorRetrievalHelper/README.md @@ -0,0 +1,162 @@ +## BeanFactoryAdvisorRetrievalHelper + +- [BeanFactoryAdvisorRetrievalHelper](#beanfactoryadvisorretrievalhelper) + - [一、基本信息](#一基本信息) + - [二、基本描述](#二基本描述) + - [三、主要功能](#三主要功能) + - [四、最佳实践](#四最佳实践) + - [五、源码分析](#五源码分析) + - [六、常见问题](#六常见问题) + +### 一、基本信息 + +✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) + +### 二、基本描述 + +`BeanFactoryAdvisorRetrievalHelper` 类是 Spring AOP 框架中的辅助工具,用于在 Bean 工厂中检索 Advisor,这些 Advisor 定义了切面逻辑,可以在目标 Bean 的方法调用中织入相应的通知。 + +### 三、主要功能 + +1. **协助Advisor的检索** + + 帮助 Spring AOP 框架在应用程序的 Bean 工厂中查找与目标 Bean 相关的 Advisor。 + +2. **解析Advisor的Bean名称** + + 解析 Advisor 在 Spring 容器中的 Bean 名称,并根据名称从 Bean 工厂中获取相应的 Advisor 实例。 + +3. **适配不同类型的Advisor** + + 支持不同类型的 Advisor,包括前置通知(BeforeAdvice)、后置通知(AfterAdvice)、环绕通知(AroundAdvice)等,能够正确地应用到目标 Bean 上。 + +4. **辅助创建代理** + + + 辅助 Spring 容器创建代理对象,并将 Advisor 中定义的通知逻辑织入到目标 Bean 的方法调用中。 + +### 四、最佳实践 + +使用 `BeanFactoryAdvisorRetrievalHelper` 类来从一个默认的 Bean 工厂中检索 Advisor,并打印出这些 Advisor 的列表。首先,我们创建一个默认的 Bean 工厂,并向其注册一个名为 "myAdvisor" 的 Advisor。然后,我们创建了 `BeanFactoryAdvisorRetrievalHelper` 实例,并将 Bean 工厂传入其中。接着,通过调用 `findAdvisorBeans()` 方法,我们获取了 Bean 工厂中的 Advisor 列表,并通过循环遍历的方式打印出每个 Advisor 的信息。 + +```java +public class BeanFactoryAdvisorRetrievalHelperDemo { + + public static void main(String[] args) { + // 创建一个默认的 Bean 工厂 + DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); + // 向 Bean 工厂注册一个名为 "myAdvisor" 的 Advisor + beanFactory.registerSingleton("myAdvisor", new MyAdvisor()); + + // 创建 BeanFactoryAdvisorRetrievalHelper 实例,并传入 Bean 工厂 + BeanFactoryAdvisorRetrievalHelper helper = new BeanFactoryAdvisorRetrievalHelper(beanFactory); + // 获取 Bean 工厂中的 Advisor 列表 + List advisors = helper.findAdvisorBeans(); + // 打印 Advisors + advisors.forEach(System.out::println); + } +} +``` + +`MyAdvisor` 类是一个自定义的 Advisor,继承自 `AbstractPointcutAdvisor`,用于定义切面的逻辑。在该类中,`getPointcut()` 方法返回一个始终为真的 Pointcut,表示适用于所有的连接点;`getAdvice()` 方法返回一个空的 Advice,表示不对目标方法添加任何额外的通知逻辑。因此,该 Advisor 没有实际的业务逻辑,仅作为演示目的。 + +```java +public class MyAdvisor extends AbstractPointcutAdvisor { + + @Override + public Pointcut getPointcut() { + return Pointcut.TRUE; + } + + @Override + public Advice getAdvice() { + return Advisor.EMPTY_ADVICE; + } +} +``` + +运行结果,成功地从 Bean 工厂中获取了Advisor。 + +```java +com.xcs.spring.MyAdvisor@1f7030a6 +``` + +### 五、源码分析 + +在`org.springframework.aop.framework.autoproxy.BeanFactoryAdvisorRetrievalHelper#findAdvisorBeans`方法中,主要功能是在当前的 Bean 工厂中查找所有符合条件的 Advisor Beans。它忽略了 FactoryBeans,并排除了当前正在创建中的 Beans。该方法首先确定 Advisor Bean 的名称列表,如果尚未缓存,则通过 `BeanFactoryUtils.beanNamesForTypeIncludingAncestors()` 方法获取。然后,它遍历这些 Advisor Bean 的名称,检查它们是否符合条件,并将符合条件的 Advisor Bean 添加到结果列表中。在添加之前,它会检查该 Bean 是否当前正在创建中,如果是,则跳过。最后,返回包含所有符合条件的 Advisor Beans 的列表。 + +```java +/** + * 在当前 Bean 工厂中查找所有符合条件的 Advisor Bean, + * 忽略 FactoryBeans,并排除当前正在创建的 Bean。 + * @return {@link org.springframework.aop.Advisor} Bean 的列表 + * @see #isEligibleBean + */ +public List findAdvisorBeans() { + // 如果未缓存 Advisor Bean 的名称列表,则确定该列表。 + String[] advisorNames = this.cachedAdvisorBeanNames; + if (advisorNames == null) { + // 不要在这里初始化 FactoryBeans我们需要保持所有常规 Bean 未初始化,以便自动代理创建器应用到它们上! + advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors( + this.beanFactory, Advisor.class, true, false); + this.cachedAdvisorBeanNames = advisorNames; + } + if (advisorNames.length == 0) { + return new ArrayList<>(); + } + + List advisors = new ArrayList<>(); + // 遍历 Advisor Bean 名称列表 + for (String name : advisorNames) { + // 检查 Bean 是否符合条件 + if (isEligibleBean(name)) { + // 如果 Bean 当前正在创建中,则跳过 + if (this.beanFactory.isCurrentlyInCreation(name)) { + if (logger.isTraceEnabled()) { + logger.trace("Skipping currently created advisor '" + name + "'"); + } + } + else { + try { + // 尝试获取 Advisor Bean,并添加到列表中 + advisors.add(this.beanFactory.getBean(name, Advisor.class)); + } + catch (BeanCreationException ex) { + Throwable rootCause = ex.getMostSpecificCause(); + if (rootCause instanceof BeanCurrentlyInCreationException) { + BeanCreationException bce = (BeanCreationException) rootCause; + String bceBeanName = bce.getBeanName(); + // 如果当前 Bean 依赖于正在创建的 Bean,则跳过 + if (bceBeanName != null && this.beanFactory.isCurrentlyInCreation(bceBeanName)) { + if (logger.isTraceEnabled()) { + logger.trace("Skipping advisor '" + name + + "' with dependency on currently created bean: " + ex.getMessage()); + } + // 忽略表示对当前正在尝试进行通知的 Bean 的引用。 + // 我们希望找到除当前正在创建的 Bean 本身之外的其他 Advisor。 + continue; + } + } + // 如果获取 Advisor Bean 失败,则抛出异常 + throw ex; + } + } + } + } + return advisors; +} +``` + +### 六、常见问题 + +1. **忽略FactoryBeans** + + + 在 `findAdvisorBeans()` 方法中,该类会忽略 FactoryBeans,只处理常规的 Advisor Beans。这是因为 FactoryBeans 可能会在初始化时产生副作用,而 `BeanFactoryAdvisorRetrievalHelper` 需要保持所有常规 Beans 未初始化,以便自动代理创建器能够正确地应用于它们。 + +2. **排除当前正在创建的Beans** + + + 在遍历 Advisor Beans 名称列表时,`findAdvisorBeans()` 方法会排除当前正在创建中的 Beans。这是为了避免在 Bean 的创建过程中引入不稳定的代理逻辑。 + +3. **错误处理** + + + 当尝试获取 Advisor Bean 时,可能会抛出 `BeanCreationException` 异常。`BeanFactoryAdvisorRetrievalHelper` 需要正确处理这些异常情况,例如,当 Advisor Bean 的依赖 Bean 正在创建中时,可以选择跳过该 Advisor。 + +4. **缓存机制** + + + 为了提高性能,`BeanFactoryAdvisorRetrievalHelper` 类使用了缓存机制来存储 Advisor Bean 的名称列表。需要注意,在 Bean 工厂中添加或删除 Advisor Bean 时,需要更新缓存以确保数据的一致性。 \ No newline at end of file diff --git a/spring-aop/spring-aop-beanFactoryAdvisorRetrievalHelper/pom.xml b/spring-aop/spring-aop-beanFactoryAdvisorRetrievalHelper/pom.xml new file mode 100644 index 0000000..ffc3337 --- /dev/null +++ b/spring-aop/spring-aop-beanFactoryAdvisorRetrievalHelper/pom.xml @@ -0,0 +1,14 @@ + + + + com.xcs.spring + spring-aop + 0.0.1-SNAPSHOT + + + 4.0.0 + spring-aop-beanFactoryAdvisorRetrievalHelper + + \ No newline at end of file diff --git a/spring-aop/spring-aop-beanFactoryAdvisorRetrievalHelper/src/main/java/com/xcs/spring/BeanFactoryAdvisorRetrievalHelperDemo.java b/spring-aop/spring-aop-beanFactoryAdvisorRetrievalHelper/src/main/java/com/xcs/spring/BeanFactoryAdvisorRetrievalHelperDemo.java new file mode 100644 index 0000000..95ee8f5 --- /dev/null +++ b/spring-aop/spring-aop-beanFactoryAdvisorRetrievalHelper/src/main/java/com/xcs/spring/BeanFactoryAdvisorRetrievalHelperDemo.java @@ -0,0 +1,24 @@ +package com.xcs.spring; + +import org.springframework.aop.Advisor; +import org.springframework.aop.framework.autoproxy.BeanFactoryAdvisorRetrievalHelper; +import org.springframework.beans.factory.support.DefaultListableBeanFactory; + +import java.util.List; + +public class BeanFactoryAdvisorRetrievalHelperDemo { + + public static void main(String[] args) { + // 创建一个默认的 Bean 工厂 + DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); + // 向 Bean 工厂注册一个名为 "myAspect" 的 Advisor + beanFactory.registerSingleton("myAspect", new MyAdvisor()); + + // 创建 BeanFactoryAdvisorRetrievalHelper 实例,并传入 Bean 工厂 + BeanFactoryAdvisorRetrievalHelper helper = new BeanFactoryAdvisorRetrievalHelper(beanFactory); + // 获取 Bean 工厂中的 Advisor 列表 + List advisors = helper.findAdvisorBeans(); + // 打印 Advisors + advisors.forEach(System.out::println); + } +} diff --git a/spring-aop/spring-aop-beanFactoryAdvisorRetrievalHelper/src/main/java/com/xcs/spring/MyAdvisor.java b/spring-aop/spring-aop-beanFactoryAdvisorRetrievalHelper/src/main/java/com/xcs/spring/MyAdvisor.java new file mode 100644 index 0000000..df3aca9 --- /dev/null +++ b/spring-aop/spring-aop-beanFactoryAdvisorRetrievalHelper/src/main/java/com/xcs/spring/MyAdvisor.java @@ -0,0 +1,19 @@ +package com.xcs.spring; + +import org.aopalliance.aop.Advice; +import org.springframework.aop.Advisor; +import org.springframework.aop.Pointcut; +import org.springframework.aop.support.AbstractPointcutAdvisor; + +public class MyAdvisor extends AbstractPointcutAdvisor { + + @Override + public Pointcut getPointcut() { + return Pointcut.TRUE; + } + + @Override + public Advice getAdvice() { + return Advisor.EMPTY_ADVICE; + } +} diff --git a/spring-aop/spring-aop-beanFactoryAspectJAdvisorsBuilder/README.md b/spring-aop/spring-aop-beanFactoryAspectJAdvisorsBuilder/README.md index 47cd561..5dee7ab 100644 --- a/spring-aop/spring-aop-beanFactoryAspectJAdvisorsBuilder/README.md +++ b/spring-aop/spring-aop-beanFactoryAspectJAdvisorsBuilder/README.md @@ -85,6 +85,13 @@ public class MyService { } ``` +运行结果,显示了两个 Advisor 对象的信息,它们分别对应着切面类 `MyAspect` 中的 `before()` 和 `after()` 方法,并针对相同的切点表达式 `execution(* com.xcs.spring.MyService.doSomething(..))`。 + +```java +InstantiationModelAwarePointcutAdvisor: expression [execution(* com.xcs.spring.MyService.doSomething(..))]; advice method [public void com.xcs.spring.MyAspect.before()]; perClauseKind=SINGLETON +InstantiationModelAwarePointcutAdvisor: expression [execution(* com.xcs.spring.MyService.doSomething(..))]; advice method [public void com.xcs.spring.MyAspect.after()]; perClauseKind=SINGLETON +``` + ### 五、源码分析 在`org.springframework.aop.aspectj.annotation.BeanFactoryAspectJAdvisorsBuilder#buildAspectJAdvisors`方法中,主要负责在当前的 Bean 工厂中查找使用 AspectJ 注解标记的切面 Bean,并将其转换为 Spring AOP Advisors 的列表。它遍历所有的 Bean 名称,识别切面 Bean,并根据其实例化模型(单例或多例)创建对应的 AspectJ Advisors。在处理过程中,还会缓存单例切面的 Advisors,以提高性能。