定制化Bean的利器:BeanPostProcessor、BeanFactoryPostProcessor

前言:

    Spring的面试中,一般都会问到IOC和AOP,大部分同学都能回答出这些知识点的基本运用,如果再多问一句,AOP的底层实现方式,大部分人都会回答动态代理。那么动态代理是如何被使用到Bean上的呢?到这里可能要刷掉一大部分人,如果没有看过Spring的源码的话,这个是比较难回答的。

    实际就是今天要了解的BeanPostProcessor,这是一个比较神奇的接口,实现AOP功能主要就是依靠这个接口。

    在Spring官方文档中,这两个知识点被当做扩展知识点来介绍的。

1.BeanPostProcessor介绍

    该接口定义了两个回调方法用于用户实现,我们可以在里面定义一系列的实现。

public interface BeanPostProcessor {

	Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;

	Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}

     如果我们自定义了BeanPostProcessor的实现,并注册到容器中,则bean的加载过程中,相关方法的调用顺序为:

    * 容器实例化bean 

    * 调用BeanPostProcessor.postProcessBeforeInitialization()前置方法

    * 调用bean的初始化方法

    * 调用BeanPostProcessor.postProcessAfterInitialization()后置方法

    Spring提供了这么一个接口,允许我们自定义实现,我们可以在自己的实现中修改bean相关属性,打印日志等一系列操作。

    下面通过一个示例看下BeanPostProcessor的使用

    1)定义BeanPostProcessor实现

public class HelloBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("before...");
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("after...");

        if (bean instanceof Student){
            bean = (Student)bean;
            ((Student) bean).setName("test:" + ((Student) bean).getName());
        }
        return bean;
    }
}

    2)定义测试Bean

@Data
public class Student implements Serializable {
    private static final long serialVersionUID = -2088281526481179972L;
    private int id;
    private String name;
    private int age;
    public Student(int id, String name, int age) {
        this.id = id;
        this.name = name;
        this.age = age;
        System.out.println("allArgsConstructor...");
    }
    public Student() {
        System.out.println("noArgsConstructor...");
    }
    public void init(){
        System.out.println("student init...");
    }

    public void destroy(){
        System.out.println("student destroy...");
    }

}

    3)将bean添加到容器中,使用JavaConfig的方式

@Configuration
public class SpringConfiguration {

    @Bean(name = "stu",autowire = Autowire.BY_TYPE,initMethod = "init")
    @Scope(value = "singleton")
    public Student student(){
        return new Student(11,"jack",22);
    }

    @Bean
    public HelloBeanPostProcessor helloBeanPostProcessor(){
        return new HelloBeanPostProcessor();
    }
}

    4)测试

@Test
public void testProcessor(){
    AnnotationConfigApplicationContext applicationContext
        = new AnnotationConfigApplicationContext(SpringConfiguration.class);
    Student bean = applicationContext.getBean(Student.class);
    System.out.println(bean);
}
// res
allArgsConstructor...
before...
16:55:14,929 DEBUG main support.DefaultListableBeanFactory:1731 - Invoking init method  'init' on bean with name 'stu'
student init...
after...
    
16:47:19,927 DEBUG main support.DefaultListableBeanFactory:483 - Finished creating instance of bean 'stu'
16:47:19,928 DEBUG main support.DefaultListableBeanFactory:251 - Returning cached instance of singleton bean 'helloBeanPostProcessor'
16:47:19,936 DEBUG main support.DefaultListableBeanFactory:251 - Returning cached instance of singleton bean 'org.springframework.context.event.internalEventListenerFactory'
16:47:19,990 DEBUG main support.DefaultListableBeanFactory:251 - Returning cached instance of singleton bean 'lifecycleProcessor'
16:47:20,003 DEBUG main support.DefaultListableBeanFactory:251 - Returning cached instance of singleton bean 'stu'
    
Student(id=11, name=test:jack, age=22)

    总结:通过结果可以看到我们之前所定义的结论是正确的。

    先调用构造方法实例化bean,然后是BeanPostProcessor的before方法,紧接着就是Bean的初始化方法(本例中的Student.init),最后就是BeanPostProcessor的after方法

2.BeanFactoryPostProcessor

    之前面试的时候被问到过这个接口,当场就蒙了。

    这个接口与我们上面的BeanPostProcessor只差了一个Factory,那么它们的功能呢?有什么相似点,又有什么不同点呢?

    我们来看下Spring官方文档对其的描述

BeanFactoryPostProcessor operates on the bean configuration metadata; that is, the Spring IoC container allows a BeanFactoryPostProcessor to read the configuration metadata and potentially change it before the container instantiates any beans other than BeanFactoryPostProcessors.

    主要就是说:BeanFactoryPostProcessor主要操作的是bean的元数据。

    什么是bean的元数据?我们可以理解为这个bean有哪些属性,属性的类型,属性的scope等等。

    下面我们就来测试一下

    1)实现接口BeanFactoryPostProcessor

public class HelloBeanFactoryPostProcessor  implements BeanFactoryPostProcessor {

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        System.out.println("BeanFactoryPostProcessor.postProcessBeanFactory()");

        String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames();
        for (String beanName: beanDefinitionNames) {
            if ("stu".equals(beanName)){
                BeanDefinition student = beanFactory.getBeanDefinition(beanName);
                MutablePropertyValues propertyValues = student.getPropertyValues();

                // 修改scope
                student.setScope("prototype");
            }
        }
    }
}

    我们在这里修改一下stu这个bean的scope,原来为singleton,现在修改为prototype

    2)在SpringConfiguration中添加该bean

     @Bean
    public HelloBeanFactoryPostProcessor helloBeanFactoryPostProcessor(){
        return new HelloBeanFactoryPostProcessor();
    }

    3)测试

@Test
public void testProcessor(){
    AnnotationConfigApplicationContext applicationContext
        = new AnnotationConfigApplicationContext(SpringConfiguration.class);
    Student bean = applicationContext.getBean(Student.class);

    Student bean2 = applicationContext.getBean(Student.class);
    System.out.println(bean);
    System.out.println(bean == bean2);
}

// res
allArgsConstructor...
before...
student init...
after...
Student(id=11, name=test:jack, age=22)
false

    可以看到,最终返回的是false,说明两次生成的对象不是同一个对象,也说明我们修改stu1这个bean的scope成功了。

3.官方对这两个接口的应用

    BeanPostProcessor:AOP

    BeanFactoryPostProcessor:PropertyPlaceholderConfigurer

    以后笔者再详细分析一下PropertyPlaceholderConfigurer的应用

参考:

https://docs.spring.io/spring/docs/4.3.23.RELEASE/spring-framework-reference/htmlsingle/ 

代码地址:https://github.com/kldwz/springstudy  

发布了122 篇原创文章 · 获赞 119 · 访问量 12万+

猜你喜欢

转载自blog.csdn.net/qq_26323323/article/details/90137015
今日推荐