Spring的BeanPostProcesser接口介绍

前言

废话不多说,直接进入主题。

同学们有想过这么一种情况吗:Spring容器提供给我们的一些接口实现类并不能满足我们的要求,但是我们又不想重新写一个类,只想在原来类上修改一些属性?

举个例子,SpringMVC中通过<mvc:annotation-driven>标签自动生成的RequestMappingHandlerAdapter有个HandlerMethodArgumentResolverComposite类型的argumentResolvers属性,这个属性内部有个HandlerMethodArgumentResolver集合属性,最终会使用这个集合处理Controller中参数的问题。这部分的知识请参考:详解SpringMVC中Controller的方法中参数的工作原理

我们通过源码来看下这个属性的初始化过程:

Spring的BeanPostProcesser接口介绍

Spring的BeanPostProcesser接口介绍  

然后处理参数的时候会遍历HandlerMethodArgumentResolver集合属性,这样自定义的HandlerMethodArgumentResolver的优先级就落后了。

如果我们想让自定义的HandlerMethodArgumentResolver在优先级提高,怎么办呢?   可以使用BeanPostProcessor接口实现。

Spring官方文档对BeanPostProcessor接口的定义:Factory hook that allows for custom modification of new bean instances, e.g. checking for marker interfaces or wrapping them with proxies.   简单翻译下:一个工厂钩子,允许对工厂中的bean实例进行自定义修改,比如标记接口或使用代理类包装bean。

实例讲解

首先看下BeanPostProcessor接口的定义:

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

接口很简单,2个方法。 看名字也知道,初始化之前的操作和初始化之后的操作。

参数bean代表工厂中的实例;beanName代表这个实例的名字;返回值代表最终使用的beanName这个名字的实例,可以用个包装类,也可以用原先的那个bean。

 

这里的实例就是我们要修改RequestMappingHandlerAdapter中argumentResolvers属性里的HandlerMethodArgumentResolver集合顺序。

BeanPostProcessor的实现类代码:

@Component
public class HandlerAdapterPostProcessor implements BeanPostProcessor{
  
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        //初始化之前不改变,原bean返回
        return bean;
    }
    
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if(bean instanceof RequestMappingHandlerAdapter && beanName.equals("org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#0")) {
            RequestMappingHandlerAdapter adapter = (RequestMappingHandlerAdapter) bean;
            List<HandlerMethodArgumentResolver> resolvers = adapter.getArgumentResolvers();
            //这里的resolvers是一个UnmodifiableList,因此需要重新new一个其他类型的List
            List<HandlerMethodArgumentResolver> newList = new ArrayList(resolvers);
            newList.add(0, new FormObjArgumentResolver());
            adapter.setArgumentResolvers(Collections.unmodifiableList(newList));
        }
        return bean;
    }
  
}

这里我们判断RequestMappingHandlerAdapter的时候根据beanName为org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#0的进行判断。

这里简单说明一下这个beanName的问题,为什么我们写成这样: 楼主的配置文件中定义了1个RequestMappingHandlerAdapter(没有写id属性),又写了<mvc:annotation-driven/>这句配置,且自定义的配置顺序在<mvc:annotation-driven/>之前。 这样就产生了2个RequestMappingHandlerAdapter,name分别为org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#0和org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#1。

当然可以给自定义的RequestMappingHandlerAdapter配置id属性,这样自定义的RequestMappingHandlerAdapter的beanName为配置的id属性,而<mvc:annotation-driven/>配置的RequestMappingHandlerAdapter的beanName为org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#0。参考资料:Spring中Ordered接口简介

注意,我们加了@Component注解,在配置文件中需要配置component-scan扫描到这个类,Spring容器会自动查询实现了BeanPostProcessor接口的实现类并执行该接口定义的方法。

我们这里仅仅在第一个位置加入了FormObjArgumentResolver这个自定义的实现HandlerMethodArgumentResolver接口的类。

结果:

Spring的BeanPostProcesser接口介绍

总结

在这里再次感叹Spring框架的强大,Spring预留给我们实现的接口太多了。 很多地方只需要实现某些接口,就会默认Spring的默认行为,而无需修改源码。 赞!

Spring的BeanPostProcesser接口介绍,布布扣,bubuko.com

Spring的BeanPostProcesser接口介绍

上一篇:【转载】Spring加载resource时classpath*:与classpath:的区别


下一篇:Lombok 安装、入门 - 消除冗长的 java 代码(转)