【spring源码】五、工厂后置处理器BeanFactoryPostProcessor【重要】

本文深入探讨Spring工厂后置处理器,介绍其基础知识,包括BeanFactoryPostProcessor和BeanDefinitionRegistryPostProcessor的作用。通过测试代码展示不同方式注册工厂后置处理器,观察bd map变化和执行时机。还对ConfigurationClassPostProcessor进行解读,分析AOP注解时机及整合源码的方法,最后探讨AnnotatedBeanDefinitionReader的作用和应用场景。

本文地址https://blog.youkuaiyun.com/hancoder/article/details/111413351

说明,本文参考了子路老师博客https://blog.youkuaiyun.com/java_lyvee/category_7351027.html,但自己也看过很多这方面的视频,包括尚硅谷的注解驱动开发,所以加了些自己的说明,尽量简洁。个人认为此文是我近期写的最好的一篇了,我感觉快要打通ssm源码整合了。

按理说工厂后置处理器没有getBean重点,但是我读了那么久源码迟迟有些地方不懂,就是因为当初没有读好工厂的后置处理器,一直没有搞懂注解的注册时机,再加上尚硅谷讲的@EnableAspectJAutoProxy也没说好到底怎么个注册法,看了几遍才发现是工厂后置处理器的问题。故写了此文

正在更新spring源码系统文章:

一、工厂后置处理器基础知识

工厂后置处理器的作用是新增和修改bean定义的,而spring中有两个接口与工厂后置处理器有关,分别是BeanFactoryPostProcessorBeanDefinitionRegistryPostProcessor

  • BeanFactoryPostProcessor:需要实现一个方法。只能修改bean定义
  • BeanDefinitionRegistryPostProcessor:他本身有一个需要实现方法,但因为继承了上面的BeanFactoryPostProcessor接口,所以需要实现2个方法。分别是修改和添加bean定义

在实际开发中用的比较少,但各个框架源码整合中用的比较多,这里我们就不写用法了,直接看helloworld吧。

二、测试代码

bean定义:bean Definition,我们简称bd,存放bd的map我们简写为bd map,不懂bd的可以取前文看

1 工厂后置处理器

我们首先需要了解他们的用法,如下,我分别为每个接口创建了个实现类,只做了简单打印,用来看看他当时里面有什么bean定义,并用注解@Component将他们加到spring容器中。

1.1 注解的:

这里是意思是定义BeanFactoryPostProcessor的实现类,并且在上面@Component。然后在里面的postProcessBeanFactory()方法里打印当前有多少个bd

@Component
public class MyFacPostProcess_zhujie  // 后面有_zhujie的标识
    implements BeanFactoryPostProcessor {
   
   
    
	@Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
   
   
		System.out.println("===注解的MyFacPostProcess.postProcessBeanFactory()被执行。。。");
		int count = beanFactory.getBeanDefinitionCount();//bean定义的数量
		String[] names = beanFactory.getBeanDefinitionNames();//bean定义的名字
		System.out.println("当前BeanFactory中有"+count+" 个Bean");
		System.out.println(Arrays.asList(names));
	}
}

和上面的逻辑一样,定义BeanDefinitionRegistryPostProcessor的实现类,并@Component。他们都是后置处理器,只不过BeanDefinitionRegistryPostProcessor可以新增和修改bd,而上面的BeanFactoryPostProcessor只能修改bd。

所以我们在它的新增bd方法里尝试new bd并设置class,然后用registry注册器注册到容器中

@Component
public class MyBeanDefinitionRegistryPostProcessor_zhujie 
    implements BeanDefinitionRegistryPostProcessor {
   
   

    @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
   
   
        System.out.println("===注解的MyBeanDefinitionRegistryPostProcessor.postProcessBeanDefinitionRegistry()执行。。。");
        int count = registry.getBeanDefinitionCount();//bean定义的数量
        String[] names = registry.getBeanDefinitionNames();//bean定义的名字
        System.out.println("当前BeanFactory中有"+count+" 个Bean");
        System.out.println(Arrays.asList(names));


        // 新增bd
        GenericBeanDefinition genericBeanDefinition =
            new GenericBeanDefinition() ;
        genericBeanDefinition.setBeanClass(Yellow.class);
        registry.registerBeanDefinition("yellow1",genericBeanDefinition);

    }

    @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
   
   
        System.out.println("===注解的MyBeanDefinitionRegistryPostProcessor.postProcessBeanFactory()执行。。。");
        int count = beanFactory.getBeanDefinitionCount();//bean定义的数量
        String[] names = beanFactory.getBeanDefinitionNames();//bean定义的名字
        System.out.println("当前BeanFactory中有"+count+" 个Bean");
        System.out.println(Arrays.asList(names));
    }
}
1.2 通过ac.addBeanFactoryPostProcessor添加的实体bean

这里的意思是我们又定义了上面两个接口的实现类,只不过没加@Component,一会再手动注册

注意不要加@Component注解,我们要手动注册工厂后置处理器。而且我们传入ac的时候已经是实例化好的

public class RuCanBeanDefinitionRegistryPostProcessor  // rucan的意思是说一会要手动注册
    implements BeanDefinitionRegistryPostProcessor {
   
   
    
	@Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
   
   
		System.out.println("===入参的registry.postProcessBeanDefinitionRegistry()执行。。。");
	}

	@Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
   
   
		System.out.println("===入参的registry.postProcessBeanDefinitionRegistry()执行。。。");
	}
}
public class RuCanBeanFactoryPostProcessor 
    implements BeanFactoryPostProcessor {
   
   

	@Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
   
   
		System.out.println("===入参的普通factoryPostPro.postProcessBeanDefinitionRegistry()执行。。。");
	}
}
1.3 通过ac.registerBeanDefinition添加的bd

(下面这两个类不用太在意,其实是我前期没学好弄的,但加上也无所谓,我们就能判断ac.addBeanFactoryPostProcessor和ac.registerBeanDefinition的区别了)

public class MyFacPostProcess_ShouDongZhuRu 
    implements BeanFactoryPostProcessor{
   
   

    @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
   
   
        System.out.println("===手动注入的MyFacPostProcess.postProcessBeanFactory()被执行。。。");
        int count = beanFactory.getBeanDefinitionCount();//bean定义的数量
        String[] names = beanFactory.getBeanDefinitionNames();//bean定义的名字
        System.out.println("当前BeanFactory中有"+count+" 个Bean");
        System.out.println(Arrays.asList(names));
    }
}
public class MyBeanDefinitionRegistryPostProcessor_ShouDongZhuRu 
    implements BeanDefinitionRegistryPostProcessor{
   
   

    @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
   
   
        System.out.println("===手动注入的MyBeanDefinitionRegistryPostProcessor.postProcessBeanDefinitionRegistry()执行。。。");
        int count = registry.getBeanDefinitionCount();//bean定义的数量
        String[] names = registry.getBeanDefinitionNames();//bean定义的名字
        System.out.println("当前BeanFactory中有"+count+" 个Bean");
        System.out.println(Arrays.asList(names));
        GenericBeanDefinition genericBeanDefinition =
            new GenericBeanDefinition() ;
        genericBeanDefinition.setBeanClass(Blue.class);
        registry.registerBeanDefinition("bule1",genericBeanDefinition);
    }

    @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
   
   
        System.out.println("===手动注入的MyBeanDefinitionRegistryPostProcessor.postProcessBeanFactory()执行。。。");
        int count = beanFactory.getBeanDefinitionCount();//bean定义的数量
        String[] names = beanFactory.getBeanDefinitionNames();//bean定义的名字
        System.out.println("当前BeanFactory中有"+count+" 个Bean");
        System.out.println(Arrays.asList(names));
    }

}

2 配置类

为了能扫描到上面的注解,我们定义配置类并扫描,此外在里面@Bean一个普通的car对象

@Configuration // 告诉Spring这是一个配置类
@ComponentScan(value="com.atguigu")
public class MainConfigFacPostProcesser {
   
   

	// 简单注入一个bean,观察执行时机
	@Bean
	Car Car1(){
   
   
		return new Car();
	}
}

3 启动类的改写

我们的启动类一般的写法是new AnnotationConfigApplicationContext(Config.class)这样子,但我们看一下他源码

public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
   
   
    this();
    register(componentClasses);
    refresh();
}

他分为了三个步骤,为了后续解释的需要,我们给他们分开写。另外我们应该意识到,在下面加注释就是在上面加注释,执行时机都是一样的。我们加了6个关键点,为了观察工厂的状态

  • 比较ac.registerBeanDefinition和ac.addBeanFactoryPostProcessor方法的区别
public class MainTest1 {
   
   

	public static void main(String[] args) {
   
   
		// 1 去初始化工厂
		// 打断点。下面这句就是创建了一个空工厂
		AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext
<think>首先,用户的问题是:“我想查找AOP后置处理器获取入参的方法 请问AOP后置处理器如何获取入参”。这涉及到Spring框架中的AOP(Aspect-Oriented Programming)后置处理器。 从用户提供的引用中: - 引用[1]提到在Spring源码中,注册了个AOP后置处理器,特别是在configureAutoProxyCreator方法中。 - 引用[2]讨论了AOP后置处理器如何在Bean初始化过程中工作,完成AOP动态代理。 - 引用[3]聚焦于Spring后置处理器,如BeanPostProcessor和BeanFactoryPostProcessor。 用户的问题是具体关于AOP后置处理器如何获取入参(即方法的参数)。 在Spring AOP中,后置处理器通常指的是BeanPostProcessor的实现,特别是用于AOP的代理创建器,如AnnotationAwareAspectJAutoProxyCreator。 要获取方法入参,主要是在AOP的通知(Advice)中,比如在@Before、@After等通知方法中。但在后置处理器的上下文中,我们需要理解如何通过后置处理器来访问方法参数。 关键点: - AOP后置处理器(如AnnotationAwareAspectJAutoProxyCreator)负责创建代理对象。 - 当代理对象被调用时,会触发拦截器链(Interceptor Chain),其中包含了通知(Advice)。 - 在通知中,可以通过JoinPoint或ProceedingJoinPoint来获取方法参数。 用户提到了“AOP后置处理器”,但从引用中看,它似乎是泛指用于AOP的后置处理器。在Spring中,AOP的实现依赖于BeanPostProcessor。 具体到获取入参: - 在自定义的通知方法(如使用@Aspect定义的切面)中,可以通过JoinPoint.getArgs()来获取方法参数。 - 但用户问的是后置处理器如何获取入参。这可能指的是在BeanPostProcessor的实现中如何访问Bean的属性或方法参数。 BeanPostProcessor有两个主要方法: - postProcessBeforeInitialization(Object bean, String beanName) - postProcessAfterInitialization(Object bean, String beanName) 在这些方法中,参数bean是正在初始化的Bean实例。要获取方法的入参,BeanPostProcessor本身不直接处理方法调用;它处理Bean的初始化。 对于AOP,代理创建发生在postProcessAfterInitialization方法中。当调用代理对象的方法时,拦截器链被触发。 因此,要访问方法入参,需要在AOP通知中处理,而不是在BeanPostProcessor的直接方法中。 在Spring源码中: - AnnotationAwareAspectJAutoProxyCreator是BeanPostProcessor的实现。 - 它覆盖了postProcessAfterInitialization方法,为Bean创建代理。 - 代理创建后,当方法被调用,会调用AopProxy.invoke方法,其中处理拦截器和通知。 在通知执行时,例如在MethodInterceptor中,可以通过MethodInvocation.getArguments()获取参数。 所以,回答用户问题时: - 解释AOP后置处理器(代理创建器)本身不直接获取入参。 - 入参获取发生在AOP通知执行时。 - 在切面中,使用JoinPoint.getArgs()来获取。 参考用户提供的引用: - 引用[2]提到“后置处理器做了什么动作完成了AOP动态代理”,暗示了代理的创建。 - 引用[3]讨论了BeanPostProcessor。 在回答中,需要结构化: - 使用中文回答。 - 行内数学表达式用$...$(但这里可能不涉及数学)。 - 独立公式用$$...$$(同样,可能不适用)。 - 回答结构清晰。 - 末尾添加相关问题。 - 自然地添加引用标识。 回答结构: 1. 澄清概念:解释AOP后置处理器的作用。 2. 描述如何获取入参:强调在通知中使用JoinPoint。 3. 提供示例代码。 4. 总结。 5. 添加相关问题。 示例代码:显示个简单的切面如何获取参数。 引用标识:在回答中引用提供的参考。 - 例如,提到引用[2]关于后置处理器的工作。 - 提到引用[3]关于后置处理器的分析。 最终响应: - 以用户问题开始。 - 逐步解释。 - 结束时添加相关问题。 </think>### AOP后置处理器如何获取入参的方法 在Spring框架中,AOP(面向切面编程)的后置处理器(如`BeanPostProcessor`的实现类`AnnotationAwareAspectJAutoProxyCreator`)主要负责在Bean初始化阶段创建代理对象,而不是直接处理方法调用时的入参。入参的获取通常发生在AOP的**通知(Advice)执行阶段**,通过`JoinPoint`或`ProceedingJoinPoint`接口实现。以下是详细解析: #### 1. **AOP后置处理器的角色** - AOP后置处理器(例如`AnnotationAwareAspectJAutoProxyCreator`)在Bean初始化后(`postProcessAfterInitialization`方法中)动态创建代理对象。它不直接处理或访问方法入参,而是为Bean添加代理逻辑[^2][^3]。 - 当代理对象的方法被调用时,Spring会触发个**拦截器链(Interceptor Chain)**,其中包含切面定义的通知(如`@Before`、`@Around`等)。入参的获取就发生在这个阶段[^2]。 #### 2. **获取入参的核心方法** 入参的获取依赖于`JoinPoint`接口(或其子接口`ProceedingJoinPoint`),它封装了当前连接点(如方法调用)的信息。关键步骤如下: - **在通知中使用`getArgs()`方法**: - `JoinPoint.getArgs()` 返回方法调用的参数数组。 - 示例:在`@Before`通知中,直接通过`JoinPoint`访问参数。 - **示例代码(基于Spring AOP的切面)**: ```java import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; @Aspect public class ParamAspect { @Before("execution(* com.example.service.*.*(..))") // 切点表达式 public void logParameters(JoinPoint joinPoint) { Object[] args = joinPoint.getArgs(); // 获取入参数组 System.out.println("方法入参: " + Arrays.toString(args)); } } ``` - 当目标方法被调用时,`logParameters`通知会触发,并通过`joinPoint.getArgs()`获取所有入参。 - **注意事项**: - 如果使用`@Around`通知,可以通过`ProceedingJoinPoint.getArgs()`获取参数,并支持修改参数(需谨慎使用)。 - 参数类型可通过`JoinPoint.getSignature().getParameterTypes()`进步验证。 #### 3. **为什么后置处理器本身不获取入参?** - AOP后置处理器(如`AnnotationAwareAspectJAutoProxyCreator`)的工作集中在代理创建阶段(例如,解析`@Aspect`切面并生成代理类),而方法调用时的参数绑定由Spring的**方法拦截器(MethodInterceptor)** 处理[^2][^3]。 - 源码关键点(简化): - 代理对象的invoke方法(如`JdkDynamicAopProxy.invoke()`)会调用`ReflectiveMethodInvocation`,其中封装了参数和拦截器链。 - 在`ReflectiveMethodInvocation.proceed()`中,参数通过`getArguments()`传递给通知(Advice)[^2]。 #### 4. **总结** - **直接方式**:在自定义切面通知(Advice)中使用`JoinPoint.getArgs()`获取入参。这是标准做法。 - **间接关联**:AOP后置处理器确保代理对象被创建,但入参处理由其生成的拦截器在运行时完成。 - 扩展阅:深入Spring源码,可参考`org.springframework.aop.framework.ReflectiveMethodInvocation`类处理参数的逻辑[^2][^3]。 如果您需要进步分析特定场景(如参数修改或性能优化),请提供更多细节!
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值