【Spring高级】常见Spring后处理器PostProcessor

发布于:2024-03-15 ⋅ 阅读:(69) ⋅ 点赞:(0)

常见Bean后处理器

上面已经初步了解了Bean后置处理器,其作用主要就是为Bean生命周期各个阶段提供扩展。下面就来看下一些比较重要的Bean后置处理器。

先创建几个Bean 的class,如下:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;


public class Bean1 {

    private Bean2 bean2;
    private Bean3 bean3;
    private String home;

    public Bean1() {
        System.out.println("Bean1 构造方法");
    }
    
    @Autowired
    public void setBean2(Bean2 bean2) {
        System.out.println("@Autowired生效:" + bean2);
        this.bean2 = bean2;
    }
    
    public Bean2 getBean2() {
        return bean2;
    }
    
    @Resource
    public void setBean3(Bean3 bean3) {
        System.out.println("@Resource生效:" + bean3);
        this.bean3 = bean3;
    }

    @Autowired
    public void setHome(@Value("${JAVA_HOME}") String home) {
        System.out.println("@Value生效:" + home);
        this.home = home;
    }

    /**
     *  @PostConstruct 用来标记 bean 初始化完成后的回调方法
     */
    @PostConstruct
    public void init() {
        System.out.println("@PostConstruct生效");
    }

    /**
     * @PreDestroy 用来标记 bean 销毁前的回调方法
     */
    @PreDestroy
    public void destroy() {
        System.out.println("@PreDestroy生效");
    }
  
  	@Override
    public String toString() {
        return "Bean1{" +
                "bean2=" + bean2 +
                ", bean3=" + bean3 +
                ", home='" + home + '\'' +
                '}';
    }

}

Bean1比较复杂,主要是用来验证各个注解是被哪些后置处理器处理。

其他的Bean class如下:

public class Bean2 {
    public Bean2() {
        System.out.println("Bean2 构造方法");
    }
}

public class Bean3 {
    public Bean3() {
        System.out.println("Bean3 构造方法");
    }
}

public class Bean4 {
    public Bean4() {
        System.out.println("Bean4 构造方法");
    }
}

运行我们的测试类

import org.springframework.context.support.GenericApplicationContext;


public class TestBeanPostProcessor {

    public static void main(String[] args) {
        // 使用GenericApplicationContext是因为他是一个干净的容器,他没有自动为我们添加哪些默认的后处理器,方便我们测试
        GenericApplicationContext context = new GenericApplicationContext();

        // 用原始方法注册3个bean
        context.registerBean("bean1", Bean1.class);
        context.registerBean("bean2", Bean2.class);
        context.registerBean("bean3", Bean3.class);

        // 手动初始化容器,里面有步骤会执行BeanFactory后处理器,添加Bean后处理器,初始化所有单例Bean
        context.refresh();

        Bean1 bean1 = (Bean1) context.getBean("bean1");
        System.out.println("bean1的bean2:" + bean1.getBean2());

        // 销毁容器
        context.close();

    }


}

结果如下:

Bean1 构造方法
Bean2 构造方法
Bean3 构造方法
bean1的bean2:null

可以看到,主动注册Bean时,确实得到了3个bean,但是我们在bean1中的注解的方法中都没有执行,bean2也没有注册到bean1中,这就是因为我们还没添加后置处理器。

下面看看添加后置处理器。

AutowiredAnnotationBeanPostProcessor

AutowiredAnnotationBeanPostProcessor后处理器主要负责处理带有 @Autowired@Value注解的 bean。

@Autowired 注解用于自动装配 bean,即 Spring 容器在初始化 bean 的过程中,会自动将匹配的 bean 注入到被注解的字段、构造函数或方法中。

如下,添加这个后处理器:

public static void main(String[] args) {
    // 使用GenericApplicationContext是因为他是一个干净的容器,他没有自动为我们添加哪些默认的后处理器,方便我们测试
    GenericApplicationContext context = new GenericApplicationContext();

    // 用原始方法注册3个bean
    context.registerBean("bean1", Bean1.class);
    context.registerBean("bean2", Bean2.class);
    context.registerBean("bean3", Bean3.class);

    // 添加解析@Value修饰的字符串类型的解析器
    context.getDefaultListableBeanFactory().setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
    // 添加后置处理器
    // AutowiredAnnotationBeanPostProcessor可以解析 @Autowired和@Value注解
    context.registerBean(AutowiredAnnotationBeanPostProcessor.class);


    // 手动初始化容器,里面有步骤会执行BeanFactory后处理器,添加Bean后处理器,初始化所有单例Bean
    context.refresh();

    Bean1 bean1 = (Bean1) context.getBean("bean1");
    System.out.println("bean1的bean2:" + bean1.getBean2());

    // 销毁容器
    context.close();

}

运行后打印记过如下:

Bean2 构造方法
@Autowired生效:com.cys.demo02.chapter04.Bean2@4c309d4d
@Value生效:${JAVA_HOME}
Bean3 构造方法
bean1的bean2:com.cys.demo02.chapter04.Bean2@4c309d4d

可以看到@Autowired生效,并且bean2被成功注入到bean1。

原理

看下面的例子:

import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor;
import org.springframework.context.annotation.CommonAnnotationBeanPostProcessor;
import org.springframework.context.annotation.ContextAnnotationAutowireCandidateResolver;
import org.springframework.context.support.GenericApplicationContext;


public class TestAutowiredBeanPostProcessor {

    public static void main(String[] args) {
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        // 注意下面这种方式,是直接把new出来的成品bean直接注册到beanFactory,就不会再走初始化,依赖注入等过程了
        beanFactory.registerSingleton("bean2", new Bean2());
        beanFactory.registerSingleton("bean3", new Bean3());
        beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());

        AutowiredAnnotationBeanPostProcessor autowiredAnnotationBeanPostProcessor = new AutowiredAnnotationBeanPostProcessor();
        // 因为注入的时候,需要用到bean,所以他需要beanFactory,也就是bean的提供者
        autowiredAnnotationBeanPostProcessor.setBeanFactory(beanFactory);

        Bean1 bean1 = new Bean1();
        System.out.println(bean1);
        // 在这个方法中进行了@Autowired和@Value注解的解析
        autowiredAnnotationBeanPostProcessor.postProcessProperties(null, bean1, "bean1");
        System.out.println(bean1);
    }
}

结果如下:

Bean2 构造方法
Bean3 构造方法
Bean1 构造方法
Bean1{bean2=null, bean3=null, home='null'}
@Autowired生效:com.cys.demo02.chapter04.Bean2@32eff876
@Value生效:${JAVA_HOME}
Bean1{bean2=com.cys.demo02.chapter04.Bean2@32eff876, bean3=null, home='${JAVA_HOME}'}

可以看到,核心方法就是autowiredAnnotationBeanPostProcessor.postProcessProperties。在执行此方法之前,bean1中的bean2位null,但是之后,bean2就注入到了bean1。

此方法源码如下:

@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
   // 获取注入的元数据
   InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
   try {
      // 执行注入
      metadata.inject(bean, beanName, pvs);
   }
   catch (BeanCreationException ex) {
      throw ex;
   }
   catch (Throwable ex) {
      throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
   }
   return pvs;
}

这个方法主要有两个步骤:

  1. 先获取注入的元数据
  2. 使用元数据执行注入

通过debug,可以看到元数据metadata

在这里插入图片描述

其中有个属性injectedElements正好记录了被@Autowired修饰的属性和方法。当metadata.inject方法执行时,就会遍历injectedElements,将其封装成DependencyDescriptor对象,容纳后组中使用方法beanFactory.resolveDependency()来获取到bean完成注入。具体源码比较复杂,就不一一看了。

CommonAnnotationBeanPostProcessor

CommonAnnotationBeanPostProcessor Bean 后处理器,继承自 InitDestroyAnnotationBeanPostProcessor,并且实现了 InstantiationAwareBeanPostProcessorBeanFactoryAwareSerializable 接口。这个后处理器主要负责处理一些常见的注解,如 @Resource@PostConstruct@PreDestroy@WebServiceRef@EJB

如下,添加这个后处理器:

import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
import org.springframework.context.annotation.CommonAnnotationBeanPostProcessor;
import org.springframework.context.annotation.ContextAnnotationAutowireCandidateResolver;
import org.springframework.context.support.GenericApplicationContext;


public class TestBeanPostProcessor {

    public static void main(String[] args) {
        // 使用GenericApplicationContext是因为他是一个干净的容器,他没有自动为我们添加哪些默认的后处理器,方便我们测试
        GenericApplicationContext context = new GenericApplicationContext();

        // 用原始方法注册3个bean
        context.registerBean("bean1", Bean1.class);
        context.registerBean("bean2", Bean2.class);
        context.registerBean("bean3", Bean3.class);

        // 添加解析@Value修饰的字符串类型的解析器
        context.getDefaultListableBeanFactory().setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
        // 添加后置处理器
        // AutowiredAnnotationBeanPostProcessor可以解析 @Autowired和@Value注解
        context.registerBean(AutowiredAnnotationBeanPostProcessor.class);
        // CommonAnnotationBeanPostProcessor 可以解析 @Resource、@PostConstruct、@PreDestroy
        context.registerBean(CommonAnnotationBeanPostProcessor.class);

        // 手动初始化容器,里面有步骤会执行BeanFactory后处理器,添加Bean后处理器,初始化所有单例Bean
        context.refresh();

        Bean1 bean1 = (Bean1) context.getBean("bean1");
        System.out.println("bean1的bean2:" + bean1.getBean2());

        // 销毁容器
        context.close();

    }


}

运行后打印记过如下:

Bean3 构造方法
@Resource生效:com.cys.demo02.chapter04.Bean3@10d68fcd
Bean2 构造方法
@Autowired生效:com.cys.demo02.chapter04.Bean2@6a03bcb1
@Value生效:${JAVA_HOME}
@PostConstruct生效
bean1的bean2:com.cys.demo02.chapter04.Bean2@6a03bcb1
@PreDestroy生效

可以看到@Resource、@PostConstruct、@PreDestroy都生效了。

ConfigurationPropertiesBindingPostProcessor

ConfigurationPropertiesBindingPostProcessor 是在 Spring Boot 的自动配置过程中使用的,它依赖于 Spring Boot 的其他组件。

常用于处理带有 @ConfigurationProperties 注解的类。@ConfigurationProperties 注解允许你将外部的配置属性(例如,从 application.propertiesapplication.yml 文件中)绑定到一个 POJO(Plain Old Java Object)上。这提供了一种方便的方式来管理和访问应用程序的配置信息。

对于带有 @ConfigurationProperties 注解的类,ConfigurationPropertiesBindingPostProcessor 会读取外部配置文件中与该类属性相匹配的属性,并使用 Spring 的属性绑定机制将这些属性值设置到相应的字段上。

@ConfigurationProperties 注解的类可以包含嵌套属性,即类的字段可以是另一个带有 @ConfigurationProperties 注解的类的实例。ConfigurationPropertiesBindingPostProcessor 会递归地处理这些嵌套属性,确保它们也被正确地绑定和初始化。

示例:

创建Bean4

import com.cys.demo02.Pojo.Inter;
import org.springframework.boot.context.properties.ConfigurationProperties;


@ConfigurationProperties(prefix = "java")
public class Bean4 {
    private String home;

    @Override
    public String toString() {
        return "Bean4{" +
                "home='" + home + '\'' +
                ", version='" + version + '\'' +
                '}';
    }

    public String getHome() {
        return home;
    }

    public void setHome(String home) {
        this.home = home;
    }

    public String getVersion() {
        return version;
    }

    public void setVersion(String version) {
        this.version = version;
    }

    private String version;


    public Bean4() {
        System.out.println("Bean4 构造方法");
    }
}

测试:

package com.cys.demo02.chapter04;

import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
import org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor;
import org.springframework.context.annotation.CommonAnnotationBeanPostProcessor;
import org.springframework.context.annotation.ContextAnnotationAutowireCandidateResolver;
import org.springframework.context.support.GenericApplicationContext;


public class TestBeanPostProcessor {

    public static void main(String[] args) {
        // 使用GenericApplicationContext是因为他是一个干净的容器,他没有自动为我们添加哪些默认的后处理器,方便我们测试
        GenericApplicationContext context = new GenericApplicationContext();

        context.registerBean("bean4", Bean4.class);

        // ConfigurationPropertiesBindingPostProcessor 可解析 @ConfigurationProperties
        ConfigurationPropertiesBindingPostProcessor.register(context.getDefaultListableBeanFactory());

        // 手动初始化容器,里面有步骤会执行BeanFactory后处理器,添加Bean后处理器,初始化所有单例Bean
        context.refresh();

        Bean4 bean4 = (Bean4) context.getBean("bean4");
        System.out.println("bean4:" +bean4);

        // 销毁容器
        context.close();

    }
}

结果如下:

Bean4 构造方法
bean4:Bean4{home='/Library/Java/JavaVirtualMachines/jdk-11.0.2.jdk/Contents/Home', version='11.0.2'}

总结

  1. @Autowired 等注解的解析属于 bean 生命周期阶段(依赖注入, 初始化)的扩展功能,这些扩展功能由 bean 后处理器来完成
  2. 每个后处理器各自增强什么功能
    • AutowiredAnnotationBeanPostProcessor 解析 @Autowired 与 @Value
    • CommonAnnotationBeanPostProcessor 解析 @Resource、@PostConstruct、@PreDestroy
    • ConfigurationPropertiesBindingPostProcessor 解析 @ConfigurationProperties
  3. 另外 ContextAnnotationAutowireCandidateResolver 负责获取 @Value 的值,解析 @Qualifier、泛型、@Lazy 等

BeanFactory后处理器

BeanFactory后处理器,即BeanFactoryPostProcessor,是一个接口,它允许你在Spring容器实际实例化任何其他的bean之前,对容器进行某些修改或检查。BeanFactoryPostProcessor通常用于修改或注册bean定义,或者执行一些初始化操作,这些操作需要在所有其他bean被初始化之前完成。

BeanFactoryPostProcessor 提供了一个在 Spring 容器完全配置好 bean 但还没有开始创建 bean 实例之前的扩展点。

实现 BeanFactoryPostProcessor 接口的类必须提供 postProcessBeanFactory 方法的实现。这个方法在 Spring 容器加载 bean 定义之后,但在创建任何 bean 实例之前被调用。

示例:

引入依赖:

<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-spring</artifactId>
    <version>1.3.2</version>
</dependency>

<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.6</version>
</dependency>

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.2.6</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
</dependency>

创建component/Bean2:

package com.cys.demo02.chapter05.component;

import org.springframework.stereotype.Component;

@Component
public class Bean2 {

    public Bean2() {
        System.out.println("Bean2 构造方法");
    }

}

创建Bean1

package com.cys.demo02.chapter05;


public class Bean1 {

    public Bean1() {
        System.out.println("Bean1 构造方法");
    }

}

创建配置类Config

package com.cys.demo02.chapter05;

import com.alibaba.druid.pool.DruidDataSource;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;


@Configuration
@ComponentScan("com.cys.demo02.chapter05.component")
public class Config {

    @Bean
    public Bean1 bean1() {
        return new Bean1();
    }

    @Bean
    public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        return sqlSessionFactoryBean;
    }

    @Bean(initMethod = "init")
    public DruidDataSource dataSource() {
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setUrl("jdbc:mysql://127.0.0.1:3306/test");
        druidDataSource.setUsername("root");
        druidDataSource.setPassword("123456");
        return druidDataSource;
    }

}

测试类

import org.springframework.context.support.GenericApplicationContext;


public class TestBeanFactoryPostProcessor {

    public static void main(String[] args) {
        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean("config", Config.class);

        // 初始化容器
        context.refresh();

        for (String beanDefinitionName : context.getBeanDefinitionNames()) {
            System.out.println(beanDefinitionName);
        }

        // 销毁容器
        context.close();

    }
}

运行后发现,实际容器的beanDefinitionName中,只有我们主动注册进去的config,而config种的@Bean等注解都没有被解析。

下面看下加BeanFactory后处理器。

这里我们使用ConfigurationClassPostProcessor

ConfigurationClassPostProcessor

ConfigurationClassPostProcessorBeanFactoryPostProcessor的一个实现类,其类图如下:

在这里插入图片描述

ConfigurationClassPostProcessor 同时实现了其他Aware接口,提供了丰富功能,可处理带有 @Configuration 注解的类。如果这个类上带有 @Bean@Import@PropertySource@PropertySources@ComponentScan 等注解,则会执行组件扫描,查找并注册其他组件的Bean。

修改上面示例:

package com.cys.demo02.chapter05;

import org.springframework.context.annotation.ConfigurationClassPostProcessor;
import org.springframework.context.support.GenericApplicationContext;


public class TestBeanFactoryPostProcessor {

    public static void main(String[] args) {
        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean("config", Config.class);
        context.registerBean(ConfigurationClassPostProcessor.class);


        // 初始化容器
        context.refresh();

        for (String beanDefinitionName : context.getBeanDefinitionNames()) {
            System.out.println(beanDefinitionName);
        }

        // 销毁容器
        context.close();

    }
}

再次运行结果如下:

Bean2 构造方法
Bean1 构造方法
config
org.springframework.context.annotation.ConfigurationClassPostProcessor
bean1
sqlSessionFactoryBean
dataSource
17:46:02.631 [main] DEBUG org.springframework.context.support.GenericApplicationContext - Closing org.springframework.context.support.GenericApplicationContext@52f759d7, started on Fri Mar 01 17:46:01 CST 2024
17:46:02.636 [main] INFO com.alibaba.druid.pool.DruidDataSource - {dataSource-1} closing ...
17:46:02.639 [main] INFO com.alibaba.druid.pool.DruidDataSource - {dataSource-1} closed

我们发现:

  • ConfigurationClassPostProcessor处理器本身也作为bean被容器管理
  • Config上的@Bean注解生效,bean1、dataSource和sqlSessionFactoryBean被注入
  • @ComponentScan注解生效,bean2被注入

BeanDefinitionRegistryPostProcessor

还有一个 BeanFactoryPostProcessor 的子接口 BeanDefinitionRegistryPostProcessor,它允许你在 bean 定义加载到容器中之后,但在容器实例化任何 bean 之前,向容器手动注册额外的 bean 定义。这个接口通常用于在运行时动态地添加或修改 bean 的定义。

实现 BeanDefinitionRegistryPostProcessor 接口的类必须提供 postProcessBeanDefinitionRegistry 方法的实现。这个方法在 Spring 容器加载 bean 定义之后,但在创建任何 bean 实例之前被调用。

下面是一个简单的 BeanDefinitionRegistryPostProcessor 的例子:

import org.springframework.beans.factory.support.BeanDefinitionRegistry;  
import org.springframework.beans.factory.support.GenericBeanDefinition;  
import org.springframework.context.annotation.Configuration;  
import org.springframework.beans.factory.config.BeanDefinitionRegistryPostProcessor;  
import org.springframework.beans.BeansException;  
  
@Configuration  
public class CustomBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {  
  
    @Override  
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {  
        // 在这里可以向容器注册新的 bean 定义  
        GenericBeanDefinition beanDefinition = new GenericBeanDefinition();  
        beanDefinition.setBeanClassName("com.example.MyCustomBean");  
        beanDefinition.setScope(BeanDefinition.SCOPE_SINGLETON);  
          
        registry.registerBeanDefinition("myCustomBean", beanDefinition);  
    }  
  
    @Override  
    public void postProcessBeanFactory(org.springframework.beans.factory.config.ConfigurableListableBeanFactory beanFactory) throws BeansException {  
        // 这个方法也可以实现,但通常不是必须的,因为 postProcessBeanDefinitionRegistry 已经足够  
    }  
}

在这个例子中,CustomBeanDefinitionRegistryPostProcessor 类实现了 BeanDefinitionRegistryPostProcessor 接口,并覆盖了 postProcessBeanDefinitionRegistry 方法。在这个方法中,我们创建了一个 GenericBeanDefinition 实例,设置了 bean 的类名和作用域,然后通过 BeanDefinitionRegistryregisterBeanDefinition 方法将其注册到容器中。

BeanFactoryPostProcessor 不同,BeanDefinitionRegistryPostProcessor 只能在 Java 配置中使用,因为它依赖于 BeanDefinitionRegistry,这是 Java 配置特有的。在 XML 配置中,你通常会使用 <bean> 标签直接定义 bean,而不是使用 BeanDefinitionRegistryPostProcessor

总的来说,BeanDefinitionRegistryPostProcessor 提供了在 Spring 容器加载 bean 定义时动态注册新 bean 的能力,这使得你可以在运行时根据特定条件或逻辑来修改或扩展容器的配置。


网站公告

今日签到

点亮在社区的每一天
去签到