前面的章節中我們講過springmvc中handler,handerMapping和handlerApdater的作用和關係,那麼本章中,我們主要看一下springmvc中自帶的一些HandlerApdater的實現類,以及如何自定義一個adapter來調用controller中的方法的。
我們知道在DispatcherServlet中,發起請求後,會先通過url到handlerMapping中獲取handler,然後再拿着這個handler去所有的handlerAdapter中通過isSupport方法找到屬於自己的adapter,那後通過這個apdater指定調用handler中的那個方法來處理請求。
下面先通過一張圖片,來看一下HandlerAdapter有哪些實現類,如下圖:
下面會直接通過源碼和實例兩部分,類對每個handlerAdapter的實現類進行說明。如果對於adapter還不提明白,請看前面的文章。
- SimpleControllerHandlerAdapter
public class SimpleControllerHandlerAdapter implements HandlerAdapter { @Override public boolean supports(Object handler) { return (handler instanceof Controller); } @Override public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 只要你的controller實現了Controller接口,重寫的方法(handleRequest)就可以處理請求 return ((Controller) handler).handleRequest(request, response); } ...省略部分代碼... }
例子:
spring-mvc.xml<bean id="/hello" class="com.lhb.controller.BeanNameURLHandlerMappingController"/>
/** * 用來處理請求的handler */ public class BeanNameURLHandlerMappingController implements Controller { @Override public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception { // 接受請求後,跳轉到hello.jsp頁面,然後在頁面打印出Hello World! ModelAndView model = new ModelAndView("hello"); model.addObject("message", "Hello World!"); return model; } }
hello.jsp
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>Insert title here</title> </head> <body> <span>${message}</span> </body> </html>
驗證:瀏覽器輸入localhost:8088//hello 進入到controller的handleRequest方法爲成功.
總結:這種方式一個controller只能映射一個url的處理 - SimpleServletHandlerAdapter
/** * <p>This adapter is not activated by default; it needs to be defined as a * bean in the DispatcherServlet context. It will automatically apply to * mapped handler beans that implement the Servlet interface then. */ public class SimpleServletHandlerAdapter implements HandlerAdapter { @Override public boolean supports(Object handler) { return (handler instanceof Servlet); } @Override public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { ((Servlet) handler).service(request, response); return null; } ...省略部分... }
實例:
spring-mvc.xml<!--使用這個adapter必須要聲明一下。因爲默認是不自動激活這個adpater的--> <bean class="org.springframework.web.servlet.handler.SimpleServletHandlerAdapter"/> <!--上面apdater將會調用這個handler來處理請求,請求爲/mySimpleServlet,將跟這個controller映射,這是因爲這個寫法符合BeanNameUrlHandlerMapping的規則,會自動將id變爲url,將class作爲handler了就,再不明白看前面相關的文章--> <bean id="/mySimpleServlet" class="com.lhb.controller.SimpleServletHandlerController"/>
SimpleServletHandlerController.java
public class SimpleServletHandlerController implements Servlet{ ... 部分忽略... @Override public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException { res.getWriter().write("i am simple servlet handler controller"); res.flushBuffer(); } ... 部分忽略... }
驗證:
瀏覽器輸入:http://localhost:8088/mySimpleServlet 打印出 i am simple servlet handler controller 成功 -
HttpRequestHandlerAdapter.java
/** * * */ public class HttpRequestHandlerAdapter implements HandlerAdapter { @Override public boolean supports(Object handler) { return (handler instanceof HttpRequestHandler); } @Override public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { ((HttpRequestHandler) handler).handleRequest(request, response); return null; } ...部分省略... }
通過源碼可知,其原理跟上面的一樣,只要我們的controller(handler)實現了HttpReqeustHandler接口,那麼就會自動使用這個adapter來調用controller中的handlerRequest方法來處理請求
實例:
spring-mvc.xml<!--id的值中必須以/開頭,這樣就可以自動被BeanNameURLHandlerMapping處理,將id最爲請求,class作爲處理請求的handler類,adpater最終會調用這個handler中的handleRequest方法處理請求--> <bean id="/myhttpRequestHandler" class="com.lhb.controller.HttpRequestAdapterController" />
HttpRequestAdapterController.java
public class HttpRequestAdapterController implements HttpRequestHandler{ @Override public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.getWriter().write("i am HttpRequestAdapterController" ); response.flushBuffer(); } }
驗證:
瀏覽器輸入:http://localhost:8088/myhttpRequestHandler 打印出i am HttpRequestAdapterController 則成功 -
AnnotationMethodHandlerAdapter
/** * @deprecated as of Spring 3.2, in favor of * {@link org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter RequestMappingHandlerAdapter} */ @Deprecated public class AnnotationMethodHandlerAdapter extends WebContentGenerator implements HandlerAdapter, Ordered, BeanFactoryAware { }
從源碼註釋中可以看到,此adapter已經被廢棄,不推薦使用了,而是同過RequestMappingHandlerAdapter來代替,所以這裏就不多做介紹了。讓我們直接看RequestMappingHandlerAdapter。
-
RequestMappingHandlerAdapter->AbstractHandlerMethodAdapter
當我們使用@Controller來註解一個類,讓它成爲處理請求的handler後,那麼當使用@RequestMapping來進行url和方法映射後,當發起請求時,都是由RequestMappingHandlerAdapter這個來實現請求對應方法的調用,以及調用結果的處理等操作。源碼如下:
public abstract class AbstractHandlerMethodAdapter extends WebContentGenerator implements HandlerAdapter, Ordered { @Override public final boolean supports(Object handler) { return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler)); } @Override public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { return handleInternal(request, response, (HandlerMethod) handler); } ...省略部分... }
public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter implements BeanFactoryAware, InitializingBean { @Override protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { ModelAndView mav; checkRequest(request); //調用controller中的處理請求的方法 if (this.synchronizeOnSession) { HttpSession session = request.getSession(false); if (session != null) { Object mutex = WebUtils.getSessionMutex(session); synchronized (mutex) { mav = invokeHandlerMethod(request, response, handlerMethod); } } else { //調用controller中的處理請求的方法 mav = invokeHandlerMethod(request, response, handlerMethod); } } else { //調用controller中的處理請求的方法 mav = invokeHandlerMethod(request, response, handlerMethod); } if (!response.containsHeader(HEADER_CACHE_CONTROL)) { if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) { applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers); } else { prepareResponse(response); } } return mav; } }
上面兩塊代碼只是把如何根據請求調用controller裏面方法的原碼放了上去,從AbstractHandlerMethodAdapter 源碼可以看出,根據多態的特性,handle方法中最終調用的是子類RequestMappingHandlerAdapter 中的handleInternal來處理請求的。
實例:@Controller public class RequestMappingHandlerMappingController { @RequestMapping("/hello") @ResponseBody public String helloAndView(){ return "hello"; } }
驗證:
瀏覽器輸入: http://localhost:8088/hello 後,頁面顯示出hello
到這裏有可能會有人有疑問,前面的adatper後者handler需要在spring-mvc.xml中通過bean標籤聲明,爲什麼這裏只是寫了這麼一點代碼?這是因爲,當在spring-mvc.xml中使用<mvc:annotation-driven/>時,便會自動把RequestMappingHandlerAdapter和RequestMappingHandlerMapping注入到容器中,然後就會自動把含有RequestMapping註解方法與url映射好,發起請求時,通過RequestMappingHandlerAdapter來調用對應的處理請求的方法即可。多以就不需要在speing-mvc.xml中聲明瞭。 -
MyHandlerAdapter
爲什麼最後講這個adapter,因爲這個我麼自己定義的adapter,並不是springmvc默認自帶的.他是讓我們來舉一反三的,會用別人的,還需要學會自己做一個自己的。話不多說,線上例子,在解釋
實例:
MyHandlerAdapter.java/** * 除了handle方法,其他兩個方法從SimpleControllerHandlerAdapter複製過來就行 * */ public class MyHandlerAdapter implements HandlerAdapter { @Override public boolean supports(Object handler) { return (handler instanceof Controller); } @Override public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 這裏便是調用我們handler中處理請求的方法的地方 return ((BeanNameURLHandlerMappingController) handler).myHandleRequest(request, response); } @Override public long getLastModified(HttpServletRequest request, Object handler) { if (handler instanceof LastModified) { return ((LastModified) handler).getLastModified(request); } return -1L; } }
BeanNameURLHandlerMappingController.java
public class BeanNameURLHandlerMappingController implements Controller { /** * 自定義的MyHandlerAdapter將調用這個方法來處理 */ public ModelAndView myHandleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception { ModelAndView model = new ModelAndView("hello"); model.addObject("message", "Hello World!"); return model; } }
hello.jsp
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>Insert title here</title> </head> <body> 你好 </body> </html>
spring-mvc.xml<!--自定義的MyHandlerAdapter將調用我們這個controller的方法來處理/myHanler這個請求--> <bean id="/myHanler" class="com.lhb.controller.BeanNameURLHandlerMappingController"/> <!--這裏的id必須爲這個固定的值,因爲springmvc源碼的DispatcherServlet會在容器中找這個名字的adapter--> <bean id="handlerAdapter" class="com.lhb.MyHandlerAdapter"/>
web.xml
<servlet> <servlet-name>spring-mvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-mvc.xml</param-value> </init-param> <init-param> <!--自定義adapter必須將這個屬性設置爲false,至於爲什麼下面將放源碼解釋--> <param-name>detectAllHandlerAdapters</param-name> <param-value>false</param-value> </init-param> <load-on-startup>1</load-on-startup> <async-supported>true</async-supported> <multipart-config> <location>/tmp</location> <max-file-size>5242880</max-file-size> <max-request-size>20971520</max-request-size> <file-size-threshold>0</file-size-threshold> </multipart-config> </servlet> <servlet-mapping> <servlet-name>spring-mvc</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
驗證:
在瀏覽器輸入http://localhost:8088/myHanler,跳轉到hello.jsp頁面爲成功
現在來解釋下,在使用自定義adapter時,必須在web.xml中將detectAllHandlerAdapters屬性設置爲false,並且在spring-mvc.xml中爲什麼聲明我們自定義的adatper時,id必須寫爲handlerAdapter,看下面DispatcherServlet的原碼:public static final String HANDLER_ADAPTER_BEAN_NAME = "handlerAdapter"; private void initHandlerAdapters(ApplicationContext context) { this.handlerAdapters = null; //是否檢測所有的adapter,如果爲true便會將容器中所有的adatper拿出來 if (this.detectAllHandlerAdapters) { Map<String, HandlerAdapter> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false); } // 如果爲false,那麼就只會在容器中查找那個名爲handlerAdapter的adpater else { HandlerAdapter ha = context.getBean(HANDLER_ADAPTER_BEAN_NAME, HandlerAdapter.class); this.handlerAdapters = Collections.singletonList(ha); } }
從源碼看出,如果只想使用我們自定義的這個adapter的話,那麼就必須通過web.xml現將detectAllHandlerAdapters設置爲false。然後便會從容器找我們聲明的名字爲handlerAdapter的那個adapter了。