SpringMVC中的HandlerAdapter解析
HandleAdapter
HandlerAdapter的功能實(shí)際就是執(zhí)行我們的具體的Controller、Servlet或者HttpRequestHandler中的方法。
類結(jié)構(gòu)如下:
1、SimpleServletHandlerAdapter實(shí)際就是執(zhí)行HttpServlet的service方法
2、SimpleControllerHandlerAdapter實(shí)際就是執(zhí)行Controller的handleRequest方法
3、HttpRequestHandlerAdapter實(shí)際就是執(zhí)行HttpRequestHandler的handleRequest方法
4、RequestMappingHandlerAdapter實(shí)際就是執(zhí)行@RequestMapping注解的方法。
5、AnnotationMethodHandlerAdapter已結(jié)被廢棄,就不做過多介紹
該接口有3個方法
public interface HandlerAdapter {
boolean supports(Object handler);
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
long getLastModified(HttpServletRequest request, Object handler);
} HandlerAdapter的執(zhí)行操作,其執(zhí)行過程在DispatcherServlet的doDispatch中,執(zhí)行流程如下:
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
........
try {
try {
//獲取合適的HandlerAdapter實(shí)現(xiàn)類
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
........
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
}
........
//執(zhí)行真正的請求操作
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
........
} HandlerAdapter處理3步
1.DispatcherServlte會根據(jù)配置文件信息注冊HandlerAdapter。
如果在配置文件中沒有配置,那么DispatcherServlte會獲取HandlerAdapter的默認(rèn)配置, 如果是讀取默認(rèn)配置的話,DispatcherServlte會讀取DispatcherServlte.properties文件, 該文件中配置了三種HandlerAdapter:HttpRequestHandlerAdapter,SimpleControllerHandlerAdapter和AnnotationMethodHandlerAdapter。 DispatcherServlte會將這三個HandlerAdapter對象存儲到它的handlerAdapters這個集合屬性中,這樣就完成了HandlerAdapter的注冊。
2.DispatcherServlte會根據(jù)handlerMapping傳過來的controller與已經(jīng)注冊好了的HandlerAdapter一一匹配, 看哪一種HandlerAdapter是支持該controller類型的,如果找到了其中一種HandlerAdapter是支持傳過來的controller類型, 那么該HandlerAdapter會調(diào)用自己的handle方法, handle方法運(yùn)用java的反射機(jī)制執(zhí)行controller的具體方法來獲得ModelAndView, 例如SimpleControllerHandlerAdapter是支持實(shí)現(xiàn)了controller接口的控制器,如果自己寫的控制器實(shí)現(xiàn)了controller接口,那么SimpleControllerHandlerAdapter就會去執(zhí)行自己寫控制器中的具體方法來完成請求。
分析handlerAdapter注冊
//初始化handlerAdapters 放在一個鏈表中
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");
}
}
} 3.根據(jù)handlerMapping傳過來的Handler對象與DispatcherServlet集合屬性handlerAdapter中的HandlerAdapter一一匹配,如果有支持Handler對象的HandlerAdapter,那么HandlerAdapter就會調(diào)用自己的handle方法處理請求。
rotected 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?");
} 在運(yùn)行的過程中發(fā)現(xiàn)SimpleControllerHandlerAdapter是支持Controller類型的控制器的。
SimpleControllerHandlerAdapter
public class SimpleControllerHandlerAdapter implements HandlerAdapter {
public boolean supports(Object handler) {
return (handler instanceof Controller);
}
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return ((Controller) handler).handleRequest(request, response);
}
public long getLastModified(HttpServletRequest request, Object handler) {
if (handler instanceof LastModified) {
return ((LastModified) handler).getLastModified(request);
}
return -1L;
}
} Controller源碼,Controller接口只有一個handleRequest方法
public interface Controller {
ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception;
} 再看看實(shí)現(xiàn)了Controller接口的AbstractController類
public abstract class AbstractController extends WebContentGenerator implements Controller {
private boolean synchronizeOnSession = false;
/**
* Set if controller execution should be synchronized on the session,
* to serialize parallel invocations from the same client.
* <p>More specifically, the execution of the <code>handleRequestInternal</code>
* method will get synchronized if this flag is "true". The best available
* session mutex will be used for the synchronization; ideally, this will
* be a mutex exposed by HttpSessionMutexListener.
* <p>The session mutex is guaranteed to be the same object during
* the entire lifetime of the session, available under the key defined
* by the <code>SESSION_MUTEX_ATTRIBUTE</code> constant. It serves as a
* safe reference to synchronize on for locking on the current session.
* <p>In many cases, the HttpSession reference itself is a safe mutex
* as well, since it will always be the same object reference for the
* same active logical session. However, this is not guaranteed across
* different servlet containers; the only 100% safe way is a session mutex.
* @see org.springframework.web.servlet.mvc.AbstractController#handleRequestInternal
* @see org.springframework.web.util.HttpSessionMutexListener
* @see org.springframework.web.util.WebUtils#getSessionMutex(javax.servlet.http.HttpSession)
*/
public final void setSynchronizeOnSession(boolean synchronizeOnSession) {
this.synchronizeOnSession = synchronizeOnSession;
}
/**
* Return whether controller execution should be synchronized on the session.
*/
public final boolean isSynchronizeOnSession() {
return this.synchronizeOnSession;
}
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response)
throws Exception {
// Delegate to WebContentGenerator for checking and preparing.
checkAndPrepare(request, response, this instanceof LastModified);
// Execute handleRequestInternal in synchronized block if required.
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
return handleRequestInternal(request, response);
}
}
}
return handleRequestInternal(request, response);
}
/**
* Template method. Subclasses must implement this.
* The contract is the same as for <code>handleRequest</code>.
* @see #handleRequest
*/
protected abstract ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
throws Exception;
} 再看一下繼承了AbstractController的MultiActionController,MultiActionController中有對AbstractController的handleRequestInternal的實(shí)現(xiàn)
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
throws Exception {
try {
//找尋該請求對象的方法名 推測是反射
String methodName = this.methodNameResolver.getHandlerMethodName(request);
//根據(jù)方法名返回視圖
return invokeNamedMethod(methodName, request, response);
}
catch (NoSuchRequestHandlingMethodException ex) {
return handleNoSuchRequestHandlingMethod(ex, request, response);
}
} 可以看出在MultiActionController的handleRequestInternal方法中分為兩步,第一步是找尋執(zhí)行該請求的方法名,第二步是調(diào)用invokeNamedMethod方法。
invokeNamedMethod方法源碼
protected final ModelAndView invokeNamedMethod(
String methodName, HttpServletRequest request, HttpServletResponse response) throws Exception {
//獲取方法對象 來自于handlerMethodMap
Method method = this.handlerMethodMap.get(methodName);
if (method == null) {
//沒有該請求方法名
throw new NoSuchRequestHandlingMethodException(methodName, getClass());
}
try {
//參數(shù)列表
Class[] paramTypes = method.getParameterTypes();
List<Object> params = new ArrayList<Object>(4);
params.add(request);
params.add(response);
if (paramTypes.length >= 3 && paramTypes[2].equals(HttpSession.class)) {
HttpSession session = request.getSession(false);
if (session == null) {
throw new HttpSessionRequiredException(
"Pre-existing session required for handler method '" + methodName + "'");
}
//session加入到params
params.add(session);
}
// If last parameter isn't of HttpSession type, it's a command.
if (paramTypes.length >= 3 &&
!paramTypes[paramTypes.length - 1].equals(HttpSession.class)) {
Object command = newCommandObject(paramTypes[paramTypes.length - 1]);
params.add(command);
//綁定
bind(request, command);
}
//執(zhí)行該方法對象的invoke
Object returnValue = method.invoke(this.delegate, params.toArray(new Object[params.size()]));
//判斷返回值類型
return massageReturnValueIfNecessary(returnValue);
}
catch (InvocationTargetException ex) {
// The handler method threw an exception.
return handleException(request, response, ex.getTargetException());
}
catch (Exception ex) {
// The binding process threw an exception.
return handleException(request, response, ex);
}
} 根據(jù)方法名在MultiActionController的方法集合屬性handlerMethodMap中尋找對應(yīng)的方法對象,然后執(zhí)行該方法對象,
執(zhí)行該方法對象后獲得一個object的返回值,通過massageReturnValueIfNecessary方法判斷這個返回值的類型,
如果這個值的返回類型是ModelAndView類型,就返回ModelAndView。到此我們找到響應(yīng)請求的方法并執(zhí)行獲得了返回值。
雖然總體走完了。 但是有兩個地方還沒有說,
1如何根據(jù)用戶發(fā)來的url請求來確定url中哪一段是執(zhí)行該請求的方法名;
2.確定方法名后是怎么找到該方法的。
handlerMethodMap咋來的
MultiActionController中有一個構(gòu)造函數(shù), registerHandlerMethods(this.delegate);方法就是對我們所寫的controller中的方法的注冊。
public MultiActionController() {
this.delegate = this;
registerHandlerMethods(this.delegate);
// We'll accept no handler methods found here - a delegate might be set later on.
} registerHandlerMethods方法
private void registerHandlerMethods(Object delegate) {
this.handlerMethodMap.clear();
this.lastModifiedMethodMap.clear();
this.exceptionHandlerMap.clear();
// Look at all methods in the subclass, trying to find
// methods that are validators according to our criteria
Method[] methods = delegate.getClass().getMethods();
for (Method method : methods) {
// We're looking for methods with given parameters.
if (isExceptionHandlerMethod(method)) {
registerExceptionHandlerMethod(method);
}
else if (isHandlerMethod(method)) {
registerHandlerMethod(method);
registerLastModifiedMethodIfExists(delegate, method);
}
}
} ParameterMethodNameResolver這個類的作用就是根據(jù)url鏈接中帶的參數(shù)來確定執(zhí)行該url的方法名是什么。在ioc容器初始ParameterMethodNameResolver的時候,容器會將“m”這個參數(shù)賦值給ParameterMethodNameResolver的屬性paramName,然后ParameterMethodNameResolver會根據(jù)url中m后面跟的參數(shù)來獲取方法名
public String getHandlerMethodName(HttpServletRequest request) throws NoSuchRequestHandlingMethodException {
String methodName = null;
// Check parameter names where the very existence of each parameter
// means that a method of the same name should be invoked, if any.
if (this.methodParamNames != null) {
for (String candidate : this.methodParamNames) {
if (WebUtils.hasSubmitParameter(request, candidate)) {
methodName = candidate;
if (logger.isDebugEnabled()) {
logger.debug("Determined handler method '" + methodName +
"' based on existence of explicit request parameter of same name");
}
break;
}
}
}
// Check parameter whose value identifies the method to invoke, if any.
if (methodName == null && this.paramName != null) {
methodName = request.getParameter(this.paramName);
if (methodName != null) {
if (logger.isDebugEnabled()) {
logger.debug("Determined handler method '" + methodName +
"' based on value of request parameter '" + this.paramName + "'");
}
}
}
if (methodName != null && this.logicalMappings != null) {
// Resolve logical name into real method name, if appropriate.
String originalName = methodName;
methodName = this.logicalMappings.getProperty(methodName, methodName);
if (logger.isDebugEnabled()) {
logger.debug("Resolved method name '" + originalName + "' to handler method '" + methodName + "'");
}
}
if (methodName != null && !StringUtils.hasText(methodName)) {
if (logger.isDebugEnabled()) {
logger.debug("Method name '" + methodName + "' is empty: treating it as no method name found");
}
methodName = null;
}
if (methodName == null) {
if (this.defaultMethodName != null) {
// No specific method resolved: use default method.
methodName = this.defaultMethodName;
if (logger.isDebugEnabled()) {
logger.debug("Falling back to default handler method '" + this.defaultMethodName + "'");
}
}
else {
// If resolution failed completely, throw an exception.
throw new NoSuchRequestHandlingMethodException(request);
}
}
return methodName;
}當(dāng)找到了方法名后,就會去MultiActionController屬性handlerMethodMap中根據(jù)方法名找方法對象。再執(zhí)行這個方法就好了。
HandlerAdapter,大家都叫它適配處理器,就是適配不同的處理器,將他們封裝起來調(diào)用同一個接口方法, 這樣DispatcherServlet就只需要調(diào)用接口方法,而不需要在DispatcherServlet判斷調(diào)用哪一個具體的HandlerAdapter的實(shí)現(xiàn)類了。
當(dāng)時看到自己項(xiàng)目里面的所有的處理器都是實(shí)現(xiàn)了MultiActionController的Controller, 再去看MultiActionController的源碼時,發(fā)現(xiàn)MultiActionController實(shí)現(xiàn)了Controller接口, Controller接口只有一個handleRequest方法, 我想DispatcherServlet直接用Controller的handleRequest方法執(zhí)行具體請求就行了,何必還要用HandlerAdapter將Controller封裝起來, 再在HandlerAdapter的handle方法里執(zhí)行Controller的handleRequest方法呢,這不是多此一舉?
只怪自己目光短淺,由于用的是配置的方式來做項(xiàng)目的, 而且平時自己寫的Controller只繼承了MultiActionController, 以為Controller接口就是所有的處理器的接口,眼里就只有Controller了。
今天再來看源碼,發(fā)現(xiàn)處理器根本就不只有Controller這一種。 還有HttpRequestHandler,Servlet等處理器。
下面來介紹一下幾種適配器對應(yīng)的處理器以及這些處理器的作用
1. AnnotationMethodHandlerAdapter主要是適配注解類處理器,注解類處理器就是我們經(jīng)常使用的@Controller的這類處理器
2. HttpRequestHandlerAdapter主要是適配靜態(tài)資源處理器,靜態(tài)資源處理器就是實(shí)現(xiàn)了HttpRequestHandler接口的處理器,這類處理器的作用是處理通過SpringMVC來訪問的靜態(tài)資源的請求。
3.SimpleControllerHandlerAdapter是Controller處理適配器,適配實(shí)現(xiàn)了Controller接口或Controller接口子類的處理器,比如我們經(jīng)常自己寫的Controller來繼承MultiActionController,那么自己寫的這些Controller就會由SimpleControllerHandlerAdapter來適配
4.SimpleServletHandlerAdapter是Servlet處理適配器,適配實(shí)現(xiàn)了Servlet接口或Servlet的子類的處理器,我們不僅可以在web.xml里面配置Servlet,其實(shí)也可以用SpringMVC來配置Servlet,不過這個適配器很少用到,而且SpringMVC默認(rèn)的適配器沒有他,默認(rèn)的是前面的三種。
適配處理器當(dāng)然用到了適配器模式。
到此這篇關(guān)于SpringMVC中的HandlerAdapter解析的文章就介紹到這了,更多相關(guān)HandlerAdapter解析內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java編程實(shí)現(xiàn)從給定范圍內(nèi)隨機(jī)N個不重復(fù)數(shù)生成隨機(jī)數(shù)的方法小結(jié)
這篇文章主要介紹了Java編程實(shí)現(xiàn)從給定范圍內(nèi)隨機(jī)N個不重復(fù)數(shù)生成隨機(jī)數(shù)的方法,結(jié)合實(shí)例形式較為詳細(xì)的分析了java根據(jù)指定范圍生成不重復(fù)隨機(jī)數(shù)的相關(guān)操作技巧,需要的朋友可以參考下2017-04-04
SpringCloud Feign傳遞HttpServletRequest對象流程
HttpServletRequest接口的對象代表客戶端的請求,當(dāng)客戶端通過HTTP協(xié)議訪問Tomcat服務(wù)器時,HTTP請求中的所有信息都封裝在HttpServletRequest接口的對象中,這篇文章介紹了Feign傳遞HttpServletRequest對象的流程,感興趣的同學(xué)可以參考下文2023-05-05
Tomcat報錯:HTTP Status 500 (Wrapper cannot find servlet class)
這篇文章主要介紹了Tomcat報錯:HTTP Status 500 (Wrapper cannot find servlet class)解決辦法的相關(guān)資料,需要的朋友可以參考下2016-11-11
Java?深入理解創(chuàng)建型設(shè)計模式之建造者模式
建造者(Builder)模式和工廠模式的關(guān)注點(diǎn)不同:建造者模式注重零部件的組裝過程,而工廠方法模式更注重零部件的創(chuàng)建過程,但兩者可以結(jié)合使用2022-02-02
SpringBoot?Validation提示信息國際化配置方式
這篇文章主要介紹了SpringBoot?Validation提示信息國際化配置方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-02-02
Java中一個for語句導(dǎo)致無窮大死循環(huán)的例子
這篇文章主要介紹了Java中一個for語句導(dǎo)致無窮大死循環(huán)的例子,本文給出的是一個很特別的例子,這個例子會跟你所想的結(jié)果不一樣,需要的朋友可以參考下2015-06-06
Spring中使用copyProperties方法進(jìn)行對象之間的屬性賦值詳解
這篇文章主要介紹了Spring中使用copyProperties方法進(jìn)行對象之間的屬性賦值詳解,使用org.springframework.beans.BeanUtils.copyProperties方法進(jìn)行對象之間屬性的賦值,避免通過get、set方法一個一個屬性的賦值,需要的朋友可以參考下2023-12-12

