springmvc篇:【HandlerAdapter的實現類】

前面的章節中我們講過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);
    	}
    	...省略部分代碼...
    }
    從源碼的handle方法可知,只要你的Controller實現了Controller接口,並重寫接口的handleRequest(request,reponse)方法,那麼便會被這個Adapter調用。不知道Adatper的作用,請看我前面文章的介紹。

    例子:
    spring-mvc.xml
    
    <bean id="/hello" class="com.lhb.controller.BeanNameURLHandlerMappingController"/>
    BeanNameURLHandlerMappingController.java
    /**
    * 用來處理請求的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;
    	}
    	...省略部分...
    }
    根據源碼的註釋,這個adatper默認是不會被激活的,如果想使用,需要我們把它注入到容器中才行,也就是在spring-mvc.xml中通過bean標籤聲明一下。使用了這個adapter後,當我們的controller實現了Servlet接口後,那麼當瀏覽器發送請求後,便可以通過這個adatper去調用controller中的service方法去處理請求了。

    實例:

    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了。


 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章