前言
廢話不多說,直接進入主題。
同學們有想過這么一種情況嗎: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的默認行為,而無需修改源碼。 贊!