handleradapter原理_SpringMVC源码总结(一)HandlerMapping和HandlerAdapter入门

刚接触SpringMVC,对它的xml文件配置一直比较模模糊糊,最近花了一点时间稍微看了下源代码,再加上调试,开始逐渐理解它,网上的类似的内容有很多,写本文主要是自己加深一下理解。本文适合用过SpringMVC的开发者,言归正传,首先搭建一个最简单的工程体验一下。

该工程是基于maven的,pom配置不再说明,所使用的spring版本4.0.5。

首先是web.xml文件配置,最简单的配置

Java代码 46448511_1.pngweb-app PUBLIC

"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"

"http://java.sun.com/dtd/web-app_2_3.dtd" >

Archetype Created Web Application

mvc

org.springframework.web.servlet.DispatcherServlet

1

mvc

/*

然后是mvc-servlet.xml文件的配置,上面配置DispatcherServlet会默认加载[servlet-name]-servlet.xml文件。对于我的配置,会去加载mvc-servlet.xml文件。

mvc-servlet.xml文件的内容:

Java代码 46448511_1.png<?xml  version="1.0" encoding="UTF-8" ?>

xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans-3.1.xsd

http://www.springframework.org/schema/mvc

http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd

http://www.springframework.org/schema/util

http://www.springframework.org/schema/util/spring-util-2.0.xsd

http://www.springframework.org/schema/context

http://www.springframework.org/schema/context/spring-context-3.2.xsd">

zh_CN

在该配置中定义了一个HomeAction的Bean。内容为:

Java代码 46448511_1.pngpackage com.lg.mvc;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import org.springframework.web.servlet.ModelAndView;

import org.springframework.web.servlet.mvc.Controller;

public class HomeAction implements Controller{

@Override

public ModelAndView handleRequest(HttpServletRequest request,

HttpServletResponse response) throws Exception {

return new ModelAndView("hello");

}

}

这是最原始的mvc做法,要继承Controller接口,先从原始的说起,最后再过渡到@Controller和@RequestMapping注解式的配置。它在mvc-serlet.xml文件中的配置有一个关键的属性name="/index"。

WEB-INF/view目录下有一个简单的hello.html,内容为:

Java代码 46448511_1.png

hello lg !

至此该工程就写完了,部署到tomcat中,项目路径为/,运行一下。

访问 http://localhost:8080/index

46448511_2.png

至此整个工程就算搭建成功了。

下面就要说说原理了。

用过python Django框架的都知道Django对于访问方式的配置就是,一个url路径和一个函数配对,你访问这个url,就会直接调用这个函数,简单明了。对于java的面向对象来说,就要分两步走。第一步首先要找到是哪个对象,即handler,本工程的handler则是HomeAction对象。第二步要找到访问的函数,即HomeAction的handleRequest方法。所以就出现了两个源码接口 HandlerMapping和HandlerAdapter,前者负责第一步,后者负责第二步。借用网上的SpringMVC架构图。

46448511_3.png

HandlerMapping接口的实现(只举了我认识的几个) :

BeanNameUrlHandlerMapping :通过对比url和bean的name找到对应的对象

SimpleUrlHandlerMapping :也是直接配置url和对应bean,比BeanNameUrlHandlerMapping功能更多

DefaultAnnotationHandlerMapping : 主要是针对注解配置@RequestMapping的,已过时

RequestMappingHandlerMapping :取代了上面一个

HandlerAdapter 接口实现:

HttpRequestHandlerAdapter : 要求handler实现HttpRequestHandler接口,该接口的方法为                                                             void handleRequest(HttpServletRequest request, HttpServletResponse response)也就是  handler必须有一个handleRequest方法

SimpleControllerHandlerAdapter:要求handler实现Controller接口,该接口的方法为ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response),也就是本工程采用的

AnnotationMethodHandlerAdapter :和上面的DefaultAnnotationHandlerMapping配对使用的,也已过时

RequestMappingHandlerAdapter : 和上面的RequestMappingHandlerMapping配对使用,针对@RequestMapping

先简单的说下这个工程的流程,访问http://localhost:8080/index首先由DispatcherServlet进行转发,通过BeanNameUrlHandlerMapping(含有 /index->HomeAction的配置),找到了HomeAction,然后再拿HomeAction和每个adapter进行适配,由于HomeAction实现了Controller接口,所以最终会有SimpleControllerHandlerAdapter来完成对HomeAction的handleRequest方法的调度。然后就顺利的执行了我们想要的方法,后面的内容不在本节中说明。

了解了大概流程,然后就需要看源代码了。

首先就是SpringMVC的入口类,DispatcherServlet,它实现了Servlet接口,不再详细说DispatcherServlet的细节,不然又是一大堆的内容。每次请求都会调用它的doService->doDispatch,我们关注的重点就在doDispatch方法中。

Java代码 46448511_1.pngprotected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {

HttpServletRequest processedRequest = request;

HandlerExecutionChain mappedHandler = null;

boolean multipartRequestParsed = false;

WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

try {

ModelAndView mv = null;

Exception dispatchException = null;

try {

processedRequest = checkMultipart(request);

multipartRequestParsed = (processedRequest != request);

//这个是重点,第一步由HandlerMapping找到对应的handler

// Determine handler for the current request.

mappedHandler = getHandler(processedRequest);

if (mappedHandler == null || mappedHandler.getHandler() == null) {

noHandlerFound(processedRequest, response);

return;

}

// Determine handler adapter for the current request.

//这是第二步,找到合适的HandlerAdapter,然后由它来调度执行handler的方法

HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

// Process last-modified header, if supported by the handler.

String method = request.getMethod();

boolean isGet = "GET".equals(method);

if (isGet || "HEAD".equals(method)) {

long lastModified = ha.getLastModified(request, mappedHandler.getHandler());

if (logger.isDebugEnabled()) {

logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);

}

if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {

return;

}

}

if (!mappedHandler.applyPreHandle(processedRequest, response)) {

return;

}

try {

// Actually invoke the handler.

mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

}

finally {

if (asyncManager.isConcurrentHandlingStarted()) {

return;

}

}

applyDefaultViewName(request, mv);

mappedHandler.applyPostHandle(processedRequest, response, mv);

}

catch (Exception ex) {

dispatchException = ex;

}

processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);

}

catch (Exception ex) {

triggerAfterCompletion(processedRequest, response, mappedHandler, ex);

}

catch (Error err) {

triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);

}

finally {

if (asyncManager.isConcurrentHandlingStarted()) {

// Instead of postHandle and afterCompletion

mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);

return;

}

// Clean up any resources used by a multipart request.

if (multipartRequestParsed) {

cleanupMultipart(processedRequest);

}

}

}

第一步详细查看:

Java代码 46448511_1.pngprotected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {

for (HandlerMapping hm : this.handlerMappings) {

if (logger.isTraceEnabled()) {

logger.trace(

"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");

}

HandlerExecutionChain handler = hm.getHandler(request);

if (handler != null) {

return handler;

}

}

return null;

}

可以看到就是通过遍历所有已注册的HandlerMapping来找到对应的handler,然后构建出一个HandlerExecutionChain,它包含了handler和HandlerMapping本身的一些拦截器,如下

Java代码 46448511_1.pngpublic class HandlerExecutionChain {

private final Object handler;

private HandlerInterceptor[] interceptors;

private List interceptorList;

//其他代码省略

}

其中HandlerMapping的getHandler实现:

Java代码 46448511_1.pngpublic final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {

Object handler = getHandlerInternal(request);

if (handler == null) {

handler = getDefaultHandler();

}

if (handler == null) {

return null;

}

// Bean name or resolved handler?

if (handler instanceof String) {

String handlerName = (String) handler;

handler = getApplicationContext().getBean(handlerName);

}

return getHandlerExecutionChain(handler, request);

}

这里的getHandlerInternal(request)是个抽象方法,由具体的HandlerMapping来实现,获取到的handler如果为空,则获取默认配置的handler,如果handler为String类型,则表示这个则会去Spring容器里面去找这样名字的bean。

再看下BeanNameUrlHandlerMapping的getHandlerInternal(request)的具体实现(通过一系列的接口设计,之后再好好看看这个设计,到BeanNameUrlHandlerMapping这只用实现该方法中的一部分),如下

Java代码 46448511_1.pngpublic class BeanNameUrlHandlerMapping extends AbstractDetectingUrlHandlerMapping {

/**

* Checks name and aliases of the given bean for URLs, starting with "/".

*/

@Override

protected String[] determineUrlsForHandler(String beanName) {

List urls = new ArrayList();

if (beanName.startsWith("/")) {

urls.add(beanName);

}

String[] aliases = getApplicationContext().getAliases(beanName);

for (String alias : aliases) {

if (alias.startsWith("/")) {

urls.add(alias);

}

}

return StringUtils.toStringArray(urls);

}

}

这里面注释说,bean的name必须以/开头,它才处理,将信息存储在Map handlerMap中,对于本工程来说就是{'/index':HomeAction对象}。

至此这里完成了第一步,下面开始第二步,即方法HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());的具体实现:

Java代码 46448511_1.pngprotected 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 +

"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");

}

遍历所有的HandlerAdapter,判断他们是否支持这个handler。

我们来看下HttpRequestHandlerAdapter的supports(handler)方法:

Java代码 46448511_1.pngpublic class HttpRequestHandlerAdapter implements HandlerAdapter {

@Override

public boolean supports(Object handler) {

//就是判断handler是否实现了HttpRequestHandler接口

return (handler instanceof HttpRequestHandler);

}

@Override

public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)

throws Exception {

//若handler实现了HttpRequestHandler接口,则调用该接口的方法,执行我们在该方法中写的业务逻辑

((HttpRequestHandler) handler).handleRequest(request, response);

return null;

}

@Override

public long getLastModified(HttpServletRequest request, Object handler) {

if (handler instanceof LastModified) {

return ((LastModified) handler).getLastModified(request);

}

return -1L;

}

}

同理SimpleControllerHandlerAdapter也是这样类似的逻辑

Java代码 46448511_1.pngpublic 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 {

return ((Controller) handler).handleRequest(request, response);

}

@Override

public long getLastModified(HttpServletRequest request, Object handler) {

if (handler instanceof LastModified) {

return ((LastModified) handler).getLastModified(request);

}

return -1L;

}

}

剩余两个AnnotationMethodHandlerAdapter和RequestMappingHandlerAdapter就比较复杂,我也没看。

按照本工程的配置,则SimpleControllerHandlerAdapter是支持HomeAction的,然后就会执行SimpleControllerHandlerAdapter的handle(processedRequest, response, mappedHandler.getHandler())方法。本质上就会调用HomeAction实现Controller接口的方法。至此就分析完了。

了解过程了之后,然后就是最重要的也是经常配置出问题的地方。DispatcherServlet的handlerMappings和handlerAdapters的来源问题。

DispatcherServlet初始化的时候,会调用一个方法如下:

Java代码 46448511_1.pngprotected void initStrategies(ApplicationContext context) {

initMultipartResolver(context);

initLocaleResolver(context);

initThemeResolver(context);

//初始化一些HandlerMapping

initHandlerMappings(context);

//初始化一些HandlerAdapter

initHandlerAdapters(context);

initHandlerExceptionResolvers(context);

initRequestToViewNameTranslator(context);

initViewResolvers(context);

initFlashMapManager(context);

}

这里可以看到,它会初始化一些HandlerMapping和HandlerAdapter,这两个方法非常重要,理解了这两个方法你就会知道,配置不对问题出在哪里,下面具体看下这两个方法:

Java代码 46448511_1.pngprivate void initHandlerMappings(ApplicationContext context) {

this.handlerMappings = null;

if (this.detectAllHandlerMappings) {

// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.

Map matchingBeans =

BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);

if (!matchingBeans.isEmpty()) {

this.handlerMappings = new ArrayList(matchingBeans.values());

// We keep HandlerMappings in sorted order.

OrderComparator.sort(this.handlerMappings);

}

}

else {

try {

HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);

this.handlerMappings = Collections.singletonList(hm);

}

catch (NoSuchBeanDefinitionException ex) {

// Ignore, we'll add a default HandlerMapping later.

}

}

// Ensure we have at least one HandlerMapping, by registering

// a default HandlerMapping if no other mappings are found.

if (this.handlerMappings == null) {

this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);

if (logger.isDebugEnabled()) {

logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");

}

}

}

detectAllHandlerMappings是DispatcherServlet的一个属性,你是可以在web.xml中配置的,默认是true,如果为true,则会去从本工程mvc-servlet.xml文件中去探测所有实现了HandlerMapping的bean,如果有,则加入DispatcherServlet的handlerMappings中。如果detectAllHandlerMappings为false,则直接去容器中找id="handlerMapping"且实现了HandlerMapping的bean.如果以上都没找到,则会去加载默认的HandlerMapping。

Java代码 46448511_1.png/** Detect all HandlerMappings or just expect "handlerMapping" bean? */

private boolean detectAllHandlerMappings = true;

本工程由于没有配置HandlerMapping,所以它会去加载默认的,下面看看默认的配置是什么

Java代码 46448511_1.pngprotected  List getDefaultStrategies(ApplicationContext context, Class strategyInterface) {

String key = strategyInterface.getName();

//defaultStrategies存储了默认的配置

String value = defaultStrategies.getProperty(key);

if (value != null) {

String[] classNames = StringUtils.commaDelimitedListToStringArray(value);

List strategies = new ArrayList(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();

}

}

继续看看defaultStrategies是如何初始化的:

Java代码 46448511_1.pngprivate 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 {

//这里的DEFAULT_STRATEGIES_PATH就是DispatcherServlet.properties

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());

}

}

这里使用静态代码块来加载配置文件DispatcherServlet.properties,它所在位置就是和DispatcherServlet同一目录下面的,如下图所示:

46448511_4.png

该默认的配置文件的内容如下:

Java代码 46448511_1.png# Default implementation classes for DispatcherServlet's strategy interfaces.

# Used as fallback when no matching beans are found in the DispatcherServlet context.

# Not meant to be customized by application developers.

org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver

org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver

#这里就是默认的HandlerMapping的配置

org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\

org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping

#这里就是默认的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

org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver,\

org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\

org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver

org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator

org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver

org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager

也就是说,当你什么都没有配置时,默认会加载以上的配置。正是由于有了上述默认配置的BeanNameUrlHandlerMapping(它要求name必须是以/开头的),它才会存储我们在mvc-servlet.xml中配置的,同样正是由于有了SimpleControllerHandlerAdapter(由于handler实现了Controller接口,所以它的support方法支持我们的handler),才会调度执行HomeAction的handleRequest方法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值