SpringMVC中的HandlerAdapter
還記得在DispatcherServlet中SpringMVC處理請求的邏輯嗎,網上有一個非常棒的圖,闡明瞭請求分發處理的整個過程。
映射處理器HandlerMapping根據Request返回一個HandlerExecutionChain實例,HandlerExecutionChain並不是真正的處理器Handler,而是包含了Handler以及攔截器HandlerInterceptor的封裝後的調用鏈對象。系統中可能存在多個HandlerMapping,不同的HandlerMapping對應着不同的策略。DispatcherServlet根據先後順序,哪個HandlerMapping最先找到處理器,則使用當前的處理器並且立即停止繼續查找。
DispatcherServlet獲取到處理器Handler後,它並不知道如何使用這個處理器。因爲處理器可能多種多樣,沒有遵循一種標準,它只是一個Object。所以這裏需要多Handler進行適配,使得所有的處理器共同擁有一個標準的接口處理請求。Spring使用的適配器的名字就是HandlerAdapter,看它的定義:
public interface HandlerAdapter {
boolean supports(Object handler);
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
long getLastModified(HttpServletRequest request, Object handler);
}
supports方法用來判斷是否支持當前的處理器,handler方法用來處理請求返回ModelAndView。選取HandlerAdapter的方式跟HandlerMapping的方式非常相似,只不過一個是根據請求選取處理器,一個是根據處理器選取處理器的適配器。下面是選取HandlerAdapter的方法
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
for (HandlerAdapter ha : this.handlerAdapters) {
if (logger.isTraceEnabled()) {
logger.trace("Testing handler adapter [" + ha + "]");
}
if (ha.supports(handler)) {
return ha;
}
}
throw new ServletException("No adapter for handler [" + handler +
"]: Does your handler implement a supported interface like Controller?");
}
可以看出,HandlerAdapter需要與HandlerMapping配合使用,如果HandlerMapping返回的處理器沒有對應的適配器是會出現異常的。HandlerAdapter的初始化
和HandlerMapping一樣,HandlerAdapter的初始化也在DispatcherServlet的initStrategies方法中,具體初始化方法如下:
private void initHandlerAdapters(ApplicationContext context) {
this.handlerAdapters = null;
if (this.detectAllHandlerAdapters) {
// Find all HandlerAdapters in the ApplicationContext, including ancestor contexts.
Map<String, HandlerAdapter> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerAdapters = new ArrayList<HandlerAdapter>(matchingBeans.values());
// We keep HandlerAdapters in sorted order.
OrderComparator.sort(this.handlerAdapters);
}
}
else {
try {
HandlerAdapter ha = context.getBean(HANDLER_ADAPTER_BEAN_NAME, HandlerAdapter.class);
this.handlerAdapters = Collections.singletonList(ha);
}
catch (NoSuchBeanDefinitionException ex) {
// Ignore, we'll add a default HandlerAdapter later.
}
}
// Ensure we have at least some HandlerAdapters, by registering
// default HandlerAdapters if no other adapters are found.
if (this.handlerAdapters == null) {
this.handlerAdapters = getDefaultStrategies(context, HandlerAdapter.class);
if (logger.isDebugEnabled()) {
logger.debug("No HandlerAdapters found in servlet '" + getServletName() + "': using default");
}
}
}
首先嚐試向容器索取HandlerAdapter,索要的方式分爲兩種:第一種是根據類型獲取所有的HandlerAdapter類型的Bean,另一種是根據名字向容器索要名字爲“handlerAdapter”的Bean。具體使用哪種方式根據參數detectAllHandlerAdapters決定,默認爲true,可以在web.xml中設置。
若容器中沒有找到HandlerAdapter,則spring會加載使用默認的HandlerAdapter。其實不僅HandlerAdapter是這樣,DispatcherServlet的一些其它組件在某些情況也會使用默認的配置。它們通過一個共同的方法獲取:
protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
String key = strategyInterface.getName();
String value = defaultStrategies.getProperty(key);
if (value != null) {
String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
List<T> strategies = new ArrayList<T>(classNames.length);
for (String className : classNames) {
try {
Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
Object strategy = createDefaultStrategy(context, clazz);
strategies.add((T) strategy);
}
catch (ClassNotFoundException ex) {
throw new BeanInitializationException(
"Could not find DispatcherServlet's default strategy class [" + className +
"] for interface [" + key + "]", ex);
}
catch (LinkageError err) {
throw new BeanInitializationException(
"Error loading DispatcherServlet's default strategy class [" + className +
"] for interface [" + key + "]: problem with class file or dependent class", err);
}
}
return strategies;
}
else {
return new LinkedList<T>();
}
}
這個defaultStrategies加載自配置文件,加載過程如下
private static final Properties defaultStrategies;
static {
// Load default strategy implementations from properties file.
// This is currently strictly internal and not meant to be customized
// by application developers.
try {
ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
}
catch (IOException ex) {
throw new IllegalStateException("Could not load 'DispatcherServlet.properties': " + ex.getMessage());
}
}
找到對應位置的配置文件,並且找到關於HandlerAdapter的配置
org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter
如上所示,容器中HandlerAdapter的時候就會使用配置好的上面的三種HandlerAdapter。如果在spring的配置文件中加入 <mvc:annotation-driven />,則它的實現類AnnotationDrivenBeanDefinitionParser會自動向容器註冊RequestMappingHandlerAdapter和RequestMappingHandlerMapping。