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


 

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