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的默認行為,而無需修改源碼。 贊!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM