SpringBoot : 定制化Bean的利器:BeanPostProcessor & BeanFactoryPostProcessor

梦想与她 提交于 2020-01-09 13:57:14

1.美图

在这里插入图片描述

2.概述

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

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

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

3.BeanPostProcessor

3.1 定义

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

public interface BeanPostProcessor {
 
	Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
 
	Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}

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

  1. 容器实例化bean
  2. 调用BeanPostProcessor.postProcessBeforeInitialization()前置方法
  3. 调用bean的初始化方法
  4. 调用BeanPostProcessor.postProcessAfterInitialization()后置方法

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

3.2 使用

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

在这里插入图片描述

3.2.1 定义BeanPostProcessor实现

package com.spring.boot.bean;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

/**
 * Description:
 *
 * @author lcc
 * @version 1.0
 * @date 2020/1/9 11:03 上午
 */
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;
    }
}


3.2.2 定义测试Bean

package com.spring.boot.bean;

import lombok.Data;

import java.io.Serializable;

/**
 * Description:
 *
 * @author lcc
 * @version 1.0
 * @date 2020/1/9 11:04 上午
 */
@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.2.3 将bean添加到容器中,使用JavaConfig的方式

package com.spring.boot.bean;

import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;

/**
 * Description:
 *
 * @author lcc
 * @version 1.0
 * @date 2020/1/9 11:05 上午
 */
@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();
    }
}

3.2.4 测试

测试程序

package com.spring.boot.bean;

import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import static org.junit.Assert.*;

public class HelloBeanPostProcessorTest {


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

}

测试结果

11:07:52.057 [main] INFO org.springframework.context.support.PostProcessorRegistrationDelegate$BeanPostProcessorChecker - Bean 'springConfiguration' of type [com.spring.boot.bean.SpringConfiguration$$EnhancerBySpringCGLIB$$eba6958a] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
11:07:52.094 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'stu'
allArgsConstructor...
before...
student init...
after...
Student(id=11, name=test:jack, age=22)

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

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

4.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等等。

4.1 案例

下面我们就来测试一下

4.1.1 实现接口BeanFactoryPostProcessor

package com.spring.boot.bean;

import org.springframework.beans.BeansException;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;

/**
 * Description:
 *
 * @author lcc
 * @version 1.0
 * @date 2020/1/9 11:13 上午
 */
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");
            }
        }
    }

}

4.1.2 在SpringConfiguration中添加该bean

package com.spring.boot.bean;

import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;

/**
 * Description:
 *
 * @author lcc
 * @version 1.0
 * @date 2020/1/9 11:05 上午
 */
@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();
    }

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

4.1.3 测试

 @Test
    public void testProcessor1(){
        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);
    }

测试结果

11:16:12.358 [main] DEBUG org.springframework.context.annotation.AnnotationConfigApplicationContext - Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@7c16905e
11:16:12.399 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor'
11:16:12.640 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerProcessor'
11:16:12.644 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'helloBeanFactoryPostProcessor'
11:16:12.646 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'springConfiguration'
11:16:12.653 [main] INFO org.springframework.context.annotation.ConfigurationClassEnhancer - @Bean method SpringConfiguration.helloBeanFactoryPostProcessor is non-static and returns an object assignable to Spring's BeanFactoryPostProcessor interface. This will result in a failure to process annotations such as @Autowired, @Resource and @PostConstruct within the method's declaring @Configuration class. Add the 'static' modifier to this method to avoid these container lifecycle issues; see @Bean javadoc for complete details.
11:16:12.674 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerFactory'
BeanFactoryPostProcessor.postProcessBeanFactory()
11:16:12.679 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor'
11:16:12.682 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalCommonAnnotationProcessor'
11:16:12.689 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'helloBeanPostProcessor'
allArgsConstructor...
before...
student init...
after...
allArgsConstructor...
before...
student init...
after...
Student(id=11, name=test:jack, age=22)
false

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

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

BeanPostProcessor:AOP

BeanFactoryPostProcessor:PropertyPlaceholderConfigurer
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!