zoukankan      html  css  js  c++  java
  • Spring的BeanPostProcesser接口介绍

    前言

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

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

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

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

      

    然后处理参数的时候会遍历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框架的强大,Spring预留给我们实现的接口太多了。 很多地方只需要实现某些接口,就会默认Spring的默认行为,而无需修改源码。 赞!

  • 相关阅读:
    【HDOJ】3660 Alice and Bob's Trip
    【HDOJ】3652 B-number
    【HDOJ】4057 Rescue the Rabbit
    【HDOJ】2155 小黑的镇魂曲
    【HDOJ】2828 Lamp
    【HDOJ】3275 Light
    【HDOJ】2430 Beans
    【POJ】2823 Sliding Window
    CE下可用的3G
    RIL开发过程
  • 原文地址:https://www.cnblogs.com/fangjian0423/p/spring-beanPostProcessor.html
Copyright © 2011-2022 走看看