Spring后置处理器BeanPostProcessor的应用

前言

最近在恶啃Spring源码,就读到一个很有意思的东西BeanPostProcessor,正式的名字叫做Spring后置处理器,这个东西非常的强大,强大到可以让我们干预Bean的创建过程,写出来分享给大家。更多Spring内容进入【Spring解读系列目录】

BeanPostProcessor

BeanPostProcessor是Spring框架提供的一个扩展类点,叫做bean后置器。通过实现BeanPostProcessor接口,程序员就可以干预bean实例化的过程,从而减轻beanFactory的负担。这个接口可以设置多个,形成一个列表,然后依次执行。注意凡是实现BeanPostProcessor接口的全部都会执行一遍。那么这个东西这么牛逼,它有什么Spring的应用实例嘛?其实我们一直用的AOP就是用这个接口在Bean实例化期间,将切面逻辑植入Bean实例中的。也正是通过BeanPostProcessor,Spring把AOP,动态代理,IOC容器建立起了联系。

操作BeanPostProcessor

上面吹了一堆这个东西这么牛逼,就得实际看看这个东西道理怎么样。于是我们就得
在这里插入图片描述
假如有个配置类AppConfig,再整一个业务类IndexDao,还有一个测试类:

@Configuration
@ComponentScan("com.demo")
public class AppConfig {
    
    
}
@Repository
public class IndexDao {
    
    
	public IndexDao() {
    
    
		System.out.println("Constructor");
	}
	@PostConstruct
	public void init(){
    
    
		System.out.println("init");
	}
	public void query(){
    
    
		System.out.println("query");
	}
}
public class Test {
    
    
	public static void main(String[] args) {
    
    
		AnnotationConfigApplicationContext anno= 
			new AnnotationConfigApplicationContext(AppConfig.class);
		IndexDao dao=anno.getBean(IndexDao.class);
		dao.query();
	}
}

这个例子里面注解@PostConstruct是Spring声明周期回调的初始化方法,笔者在【Spring生命周期回调的应用】这里有详解,有兴趣的各位可以去看下。下面我们开始正片。

BeanPostProcessor接口

新建一个类继承BeanPostProcessor接口,然后有两个实现方法,我们给实现了。因为Java 8中添加了默认的实现字符default,在最新的Spring版本中把这两个方法都加了default默认符,因此需要用快捷键把他们调出来。先讲解一下参数,第一个参数Object bean这里传入的就是我们要寻找的bean,比如我在Test里调用了IndexDao.class,在Spring容器里就会把IndexDao传到这个方法里做事情,只不过Spring调用对程序员来说是黑盒子不用管而已。第二个参数String beanName,自然就是这个bean的名字,没什么可以解释的。Spring底层存储这些bean是类似一个map存储解构存的,差不多就是map<BeanName,BeanDefinitation>这样的解构,因此这里拿到这些非常方便。我的这个例子就是当发现传入的是indexDao的时候,就执行一些事情,为了避免Spring自己的东西干扰,因此做了一个处理。

public class TestBeanPostProcessor implements BeanPostProcessor{
    
    
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    
    
        if(beanName.equals("indexDao")){
    
      //特指indexDao时触发
            System.out.println("postProcessBeforeInitialization + IndexDao");
        }
        return bean; //返回该bean
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    
    
        if(beanName.equals("indexDao")){
    
    
            System.out.println("postProcessAfterInitialization + IndexDao");
        }
        return bean;
    }
}

上面一共两个方法,看名字一个是before,一个是after。顾名思义一个是初始化之前被调用的,一个就是初始化之后被调用的。那么我们运行一下Test看看效果:

运行结果:
Constructor
postProcessBeforeInitialization + IndexDao
init
postProcessAfterInitialization + IndexDao
query

看输出确实是在初始化方法前后执行的,那么到了这里大家是否有一些对于Spring AOP的感悟,Spring AOP是怎么做的,就是创建一个新的代理返回出去对不对。我们也可以做啊,只要在bean初始化之前修改返回就可以干预这个bean的生成,比如下面的修改:

@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    
    
    if(beanName.equals("indexDao")){
    
    
        System.out.println("postProcessBeforeInitialization + IndexDao");
    }
    <这里是伪码>
    //这里可以直接把对象重新复写成代理对象返回去,这样就干预了IndexDao的初始化,变成了IndexDaoProxy。
    return Proxy.newIndexDaoProxy();
}

AOP不就是这么做的么,我们实例化一个A,但是我们拿到的是一个代理B,代理B执行了代理方法和A里面的业务方法。现在我们在实例化A的时候,对A进行加工做成B,然后把B返回出去,不就做到了Spring AOP的过程。是不是有种刷新了认知的感觉。

PriorityOrdered接口

这是一个小扩展,也是整BeanPostProcessor的时候发现的。这个接口可以改变BeanPostProcessor的顺序。怎么说呢,我们修改一下TestBeanPostProcessor让其实现PriorityOrdered接口,然后实现里面的getOrder()方法。然后再复制一个TestBeanPostProcessor1的类,修改一下输出:

 @Component
public class TestBeanPostProcessor implements BeanPostProcessor , PriorityOrdered {
    
    
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    
    
        if(beanName.equals("indexDao")){
    
    
            System.out.println("postProcessBeforeInitialization + IndexDao");
        }
        return bean;
    }
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    
    
        if(beanName.equals("indexDao")){
    
    
            System.out.println("postProcessAfterInitialization + IndexDao");
        }
        return bean;
    }
    @Override
    public int getOrder() {
    
    
        return 15; //这个值随便写的
    }
}
@Component
public class TestBeanPostProcessor1 implements BeanPostProcessor, PriorityOrdered {
    
    
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    
    
        if(beanName.equals("indexDao")){
    
    
            System.out.println("postProcessBeforeInitialization1 + IndexDao");
        }
        return bean;
    }
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    
    
        if(beanName.equals("indexDao")){
    
    
            System.out.println("postProcessAfterInitialization1 + IndexDao");
        }
        return bean;
    }
    @Override
    public int getOrder() {
    
    
        return 11;
    }
}
运行输出:
Constructor
postProcessBeforeInitialization1 + IndexDao
postProcessBeforeInitialization + IndexDao
init
postProcessAfterInitialization1 + IndexDao
postProcessAfterInitialization + IndexDao
query

对比输出结果,发现TestBeanPostProcessor1先执行了。我们接着修改TestBeanPostProcessor1.getOrder()方法,再执行:

TestBeanPostProcessor1里面的:
@Override
public int getOrder() {
    
    
    return 100;
}
运行输出:
Constructor
postProcessBeforeInitialization + IndexDao
postProcessBeforeInitialization1 + IndexDao
init
postProcessAfterInitialization + IndexDao
postProcessAfterInitialization1 + IndexDao
query

运行输出的结果就把postProcessBeforeInitialization1调到后面去了。其实PriorityOrdered就是调整Bean创建优先顺序的接口,其中getOrder()值越小,优先权越高。

结语

最近开始啃Spring源码,啃的越多也觉得自己的水平和小白越近,马上都快不会写代码了。但是源码干涩,难以下咽,等笔者消化消化有了新的发现再分享出来,有问题的地方欢迎读者老爷们指正,谢谢大家阅读。

猜你喜欢

转载自blog.csdn.net/Smallc0de/article/details/108408984