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