Spring高级篇- BeanPostProcessor和BeanFactoryPostProcessor

2022-09-27 08:48:53

Spring提供了BeanPostProcessor、BeanFactoryPostProcessor两大类后置处理器

  • BeanPostProcessor:属于 bean 生命周期阶段(依赖注入, 初始化)的扩展功能。@Autowired、@Resource、@Value 等注解的解析由不同的 Bean后处理器来完成。
  • BeanFactoryPostProcessor:主要就是补充了一些 bean 定义。@ComponentScan, @Bean, @Mapper 等注解的解析属于核心容器(即 BeanFactory)的扩展功能,这些扩展功能由不同的 BeanFactory 后处理器来完成。

Bean后置处理器

后置处理器功能
AutowiredAnnotationBeanPostProcessor解析 @Autowired 与 @Value
CommonAnnotationBeanPostProcessor解析 @Resource、@PostConstruct、@PreDestroy
ConfigurationPropertiesBindingPostProcessor解析 @ConfigurationProperties

案例演示

准备Bean

publicclassBean1{privatestaticfinalLogger log=LoggerFactory.getLogger(Bean1.class);privateBean2 bean2;@AutowiredpublicvoidsetBean2(Bean2 bean2){
        log.debug("@Autowired 生效: {}", bean2);this.bean2= bean2;}@AutowiredprivateBean3 bean3;@ResourcepublicvoidsetBean3(Bean3 bean3){
        log.debug("@Resource 生效: {}", bean3);this.bean3= bean3;}privateString home;@AutowiredpublicvoidsetHome(@Value("${JAVA_HOME}")String home){
        log.debug("@Value 生效: {}", home);this.home= home;}@PostConstructpublicvoidinit(){
        log.debug("@PostConstruct 生效");}@PreDestroypublicvoiddestroy(){
        log.debug("@PreDestroy 生效");}@OverridepublicStringtoString(){return"Bean1{"+"bean2="+ bean2+", bean3="+ bean3+", home='"+ home+'\''+'}';}}publicclassBean2{}publicclassBean3{}@ConfigurationProperties(prefix="java")publicclassBean4{privateString home;privateString version;publicStringgetHome(){return home;}publicvoidsetHome(String home){this.home= home;}publicStringgetVersion(){return version;}publicvoidsetVersion(String version){this.version= version;}@OverridepublicStringtoString(){return"Bean4{"+"home='"+ home+'\''+", version='"+ version+'\''+'}';}}

测试

publicstaticvoidmain(String[] args){//GenericApplicationContext 是一个【干净】的容器, 不会注入后置处理器GenericApplicationContext context=newGenericApplicationContext();//用原始方法注册三个 bean
        context.registerBean("bean1",Bean1.class);
        context.registerBean("bean2",Bean2.class);
        context.registerBean("bean3",Bean3.class);
        context.registerBean("bean4",Bean4.class);//@Value("${JAVA_HOME}") String home 作为方法参数解析 需要添加这个处理
        context.getDefaultListableBeanFactory().setAutowireCandidateResolver(newContextAnnotationAutowireCandidateResolver());//解析 @Autowired @Value
        context.registerBean(AutowiredAnnotationBeanPostProcessor.class);//解析 @Resource @PostConstruct @PreDestroy
        context.registerBean(CommonAnnotationBeanPostProcessor.class);//解析 @ConfigurationPropertiesConfigurationPropertiesBindingPostProcessor.register(context.getDefaultListableBeanFactory());//初始化容器
        context.refresh();// 执行beanFactory后处理器, 添加bean后处理器, 初始化所有单例System.out.println(context.getBean(Bean1.class));System.out.println(context.getBean(Bean4.class));//销毁容器
        context.close();}

@Autowired bean 后置处理器运行分析

publicclassDigInAutowired{publicstaticvoidmain(String[] args)throwsThrowable{DefaultListableBeanFactory beanFactory=newDefaultListableBeanFactory();//这样注册的bean不会进行依赖注入,初始化的过程
        beanFactory.registerSingleton("bean2",newBean2());
        beanFactory.registerSingleton("bean3",newBean3());//不加的时候解析@Value的信息会报错
        beanFactory.setAutowireCandidateResolver(newContextAnnotationAutowireCandidateResolver());AutowiredAnnotationBeanPostProcessor processor=newAutowiredAnnotationBeanPostProcessor();
        processor.setBeanFactory(beanFactory);Bean1 bean1=newBean1();System.out.println(bean1);// 执行依赖注入 @Autowired @Value
        processor.postProcessProperties(null, bean1,"bean1");System.out.println(bean1);}}

在这里插入图片描述
查看postProcessProperties源码,调用了InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);使用反射获取里面的信息,发现使用@Value @Autowired 的成员变量,方法参数信息都被封装到了InjectionMetadata对象里面。

Method findAutowiringMetadata=AutowiredAnnotationBeanPostProcessor.class.getDeclaredMethod("findAutowiringMetadata",String.class,Class.class,PropertyValues.class);
        findAutowiringMetadata.setAccessible(true);// 获取 Bean1 上加了 @Value @Autowired 的成员变量,方法参数信息InjectionMetadata metadata=(InjectionMetadata) findAutowiringMetadata.invoke(processor,"bean1",Bean1.class,null);System.out.println(metadata);

通过上面的可以知道:AutowiredAnnotationBeanPostProcessor执行依赖注入的时候

  1. 调用postProcessProperties方法,找到标注为@Value @Autowired 的成员变量和方法
  2. 调用 InjectionMetadata 来进行依赖注入, 注入时按类型查找值

    metadata.inject(bean1, “bean1”, null);

@Value的值没有解析

查看日志发现JAVA_HOME的信息没有被注入到home中,这是因为容器少加了一个解析器

// ${} 的解析器
beanFactory.addEmbeddedValueResolver(newStandardEnvironment()::resolvePlaceholders);

InjectionMetadata内部处理机制

  1. InjectionMetadata 内部根据成员变量,方法参数封装为 DependencyDescriptor 类型
  2. 有了 DependencyDescriptor,就可以利用 beanFactory.doResolveDependency 方法进行基于类型的查找
//加了 @Autowired成员变量的依赖注入Field bean3=Bean1.class.getDeclaredField("bean3");DependencyDescriptor dd1=newDependencyDescriptor(bean3,false);Object o= beanFactory.doResolveDependency(dd1,null,null,null);System.out.println(o);//加了 @Autowired方法的依赖注入Method setBean2=Bean1.class.getDeclaredMethod("setBean2",Bean2.class);DependencyDescriptor dd2=newDependencyDescriptor(newMethodParameter(setBean2,0),true);Object o1= beanFactory.doResolveDependency(dd2,null,null,null);System.out.println(o1);//加了 @Value的依赖注入Method setHome=Bean1.class.getDeclaredMethod("setHome",String.class);DependencyDescriptor dd3=newDependencyDescriptor(newMethodParameter(setHome,0),true);Object o2= beanFactory.doResolveDependency(dd3,null,null,null);System.out.println(o2);

BeanFactory后置处理器

后置处理器功能
ConfigurationClassPostProcessor可以解析@ComponentScan、@Bean、@Import、@ImportResource
MapperScannerConfigurer可以解析Mapper 接口

案例演示

pom文件

<dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.2.0</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.2.8</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency>

定义Bean

publicclassBean1{privatestaticfinalLogger log=LoggerFactory.getLogger(Bean1.class);publicBean1(){
        log.debug("我被 Spring 管理啦");}}@Configuration@ComponentScan("com.javaming.study.spring.heima.a05.component")publicclassConfig{@BeanpublicBean1bean1(){returnnewBean1();}@BeanpublicSqlSessionFactoryBeansqlSessionFactoryBean(DataSource dataSource){SqlSessionFactoryBean sqlSessionFactoryBean=newSqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);return sqlSessionFactoryBean;}@Bean(initMethod="init")publicDruidDataSourcedataSource(){DruidDataSource dataSource=newDruidDataSource();
        dataSource.setUrl("jdbc:mysql://localhost:3306/test");
        dataSource.setUsername("root");
        dataSource.setPassword("root");return dataSource;}}//component目录下定义的类@ComponentpublicclassBean2{privatestaticfinalLogger log=LoggerFactory.getLogger(Bean2.class);publicBean2(){
        log.debug("我被 Spring 管理啦");}}@ControllerpublicclassBean3{privatestaticfinalLogger log=LoggerFactory.getLogger(Bean3.class);publicBean3(){
        log.debug("我被 Spring 管理啦");}}

测试发现:只打印了config一个bean,Bean1、Bean2这些类都没有注入。

publicstaticvoidmain(String[] args)throwsIOException{GenericApplicationContext context=newGenericApplicationContext();
        context.registerBean("config",Config.class);//初始化容器
        context.refresh();for(String name: context.getBeanDefinitionNames()){System.out.println(name);}//销毁容器
        context.close();}

容器中注册ConfigurationClassPostProcessor后,

// @ComponentScan @Bean @Import @ImportResource
        context.registerBean(ConfigurationClassPostProcessor.class);

在这里插入图片描述

如何增加@Mapper的扫描

@MapperpublicinterfaceMapper1{}// @MapperScanner 指定扫描的包
context.registerBean(MapperScannerConfigurer.class, bd->{
   bd.getPropertyValues().add("basePackage","com.javaming.study.spring.heima.a05.mapper");});

在这里插入图片描述
发现除了扫描到的@Mapper文件,还会帮我们加上其他的后置处理器

  • 作者:dudu0917
  • 原文链接:https://blog.csdn.net/dudu0917/article/details/124844536
    更新时间:2022-09-27 08:48:53