Spring源码解析-springmvc
来源:互联网 发布:think php if 编辑:程序博客网 时间:2024/06/17 02:14
springmvc是spring为web应用程序提供的一个模块。
Springmvc是基于servlet实现的,通过实现Servlet接口的DispatcherServlet来封装核心功能,通过请求分派给处理程序。
在分析源码之前先来一套简单的spirngmvc程序。
首先我们需要配置web.xml文件,服务器启动后的工作就从这开始。
①
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" ><web-app> <display-name>Archetype Created Web Application</display-name> <!--spring配置文件的位置--> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param> <!--上下文加载器,在ServletContext初始化后载入配置文件--> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!--进行请求调度的servlet,在DispatcherServlet载入后会根据servlet-name的值加载xml文件,也可以自己定义,加入<init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:springmvc.xml</param-value> </init-param>--> <servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!--在这里我们自己定义配置文件的位置--> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:springmvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>springmvc</servlet-name> <url-pattern>*.htm</url-pattern> </servlet-mapping></web-app>
②创建接收参数的model
public class User { private String userName; private Integer age; public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; }}
③编写控制器
public class UserController extends AbstractController{ protected ModelAndView handleRequestInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception { List<User> userList = new ArrayList<User>(); User user = new User(); user.setUserName("小明"); user.setAge(14); User user1 = new User(); user1.setUserName("大明"); user1.setAge(15); userList.add(user); userList.add(user1); //转发到名为userList的jsp return new ModelAndView("userList","users",userList); }}
④编写jsp文件
<%-- Created by IntelliJ IDEA. User: Administrator Date: 2017/9/16 Time: 0:52 To change this template use File | Settings | File Templates.--%><%@ page contentType="text/html;charset=UTF-8" language="java" %><%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%><html><head> <title>Title</title></head><body> <c:forEach items="${users}" var="user"> <c:out value="${user.userName}"/><br/> <c:out value="${user.age}"/><br/> </c:forEach></body></html>
⑤编写springmvc的配置文件,也就是之前的springmvc.xml
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" xmlns:task="http://www.springframework.org/schema/task" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <bean id="userController" class="com.creat.controller.UserController"/> <!--编写一个简单的url映射器来找到控制器--> <bean id="simpleUrlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> <property name="mappings"> <props> <!--请求路径,以及对应的控制器映射--> <prop key="/userList.htm">userController</prop> </props> </property> </bean> <!--配置视图解析器,在ModelAndView返回的视图名上加上指定的prefix前缀和suffix后缀--> <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/jsp/"/> <property name="suffix" value=".jsp"/> </bean></beans>
⑥启动服务器,访问userList
在访问后有个小问题,c标签失效了,后来查了资料,发现:由于Jsp2.0向后兼容的特性, 当遇到使用Jsp 1.2(Servlet v2.3)的网站时会默认的禁用JSP2.0 EL,所以导致c:out不能正确输出。所以加入在jsp文件中加入<%@ page isELIgnored=”false”%>,最后得到了正确结果
源码分析
我们从web.xml配置文件中可以看出,当服务器启动时,ServletContext进行初始化时,ContextLoaderListener就会被加载,然后我们进入这个类。
/*ContextLoaderListener实现了ServletContextListener这个监听器,所以当ServletContext进行初始化时,这个类会被加载,且调用contextInitialized方法,ServletContext会在服务器启动时被加载初始化,ServletContext相当于web应用的一个全局变量,一个web应用只有一个ServletContext*/public class ContextLoaderListener extends ContextLoader implements ServletContextListener { public ContextLoaderListener() { } public ContextLoaderListener(WebApplicationContext context) { super(context); } //当ServletContext初始化时调用这个方法 public void contextInitialized(ServletContextEvent event) { this.initWebApplicationContext(event.getServletContext()); } //当ServletContext被销毁时时调用这个方法 public void contextDestroyed(ServletContextEvent event) { this.closeWebApplicationContext(event.getServletContext()); ContextCleanupListener.cleanupAttributes(event.getServletContext()); }}
①ContextLoaderListener的核心就是初始化WebApplicationContext,将其放入ServletContext
我们进入this.initWebApplicationContext(event.getServletContext());这个方法查看
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) { /*如果WebApplicationContext在ServletContext中已经存在,那么就抛出异常*/ if(servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) { throw new IllegalStateException("Cannot initialize context because there is already a root application context present - check whether you have multiple ContextLoader* definitions in your web.xml!"); } else { /*日志输出*/ Log logger = LogFactory.getLog(ContextLoader.class); servletContext.log("Initializing Spring root WebApplicationContext"); if(logger.isInfoEnabled()) { logger.info("Root WebApplicationContext: initialization started"); } long startTime = System.currentTimeMillis(); try { if(this.context == null) { /*创建并初始化WebApplicationContext*/ this.context = this.createWebApplicationContext(servletContext); }
进入这个createWebApplicationContext方法
protected WebApplicationContext createWebApplicationContext(ServletContext sc) { /*确定容器的class类*/ Class<?> contextClass = this.determineContextClass(sc);
继续进入这个determineContextClass方法
protected Class<?> determineContextClass(ServletContext servletContext) { /*这个值可以在web.xml配置是设置 <context-param> <param-name>contextClass</param-name> <paramvalue> org.springframework.web.context.WebApplicationContext </param-value> </context-param>实现*/ String contextClassName = servletContext.getInitParameter("contextClass"); /*如果不为空*/ if(contextClassName != null) { try { /*得到该class名对应的实际Class类*/ return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader()); } catch (ClassNotFoundException var4) { throw new ApplicationContextException("Failed to load custom context class [" + contextClassName + "]", var4); } } else { /*如果为空,那么从defaultStrategies中获取*/ contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName()); try { return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader()); } catch (ClassNotFoundException var5) { throw new ApplicationContextException("Failed to load default context class [" + contextClassName + "]", var5); } } }
那么defaultStrategies中是如何得到的contextClass的类名,
我们在静态代码块中找到了这个defaultStrategies的过程,
static { try { /*获取ContextLoader.properties配置文件,从配置文件中获取需要被加载的context类名*/ ClassPathResource resource = new ClassPathResource("ContextLoader.properties", ContextLoader.class); defaultStrategies = PropertiesLoaderUtils.loadProperties(resource); } catch (IOException var1) { throw new IllegalStateException("Could not load 'ContextLoader.properties': " + var1.getMessage()); } currentContextPerThread = new ConcurrentHashMap(1); }
回到之前的创建WebApplicatonContext的方法
if(!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) { throw new ApplicationContextException("Custom context class [" + contextClass.getName() + "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]"); } else { /*实例化WebApplicationContext类*/ return (ConfigurableWebApplicationContext)BeanUtils.instantiateClass(contextClass); } }
回到之前初始化context的方法
/*判断这个context是否是ConfigurableWebApplicationContext的对象,默认的context是XmlWebApplcationContext,实现了ConfigurableWebApplicationContext接口,所以正确*/, if(this.context instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext)this.context; if(!cwac.isActive()) { if(cwac.getParent() == null) { ApplicationContext parent = this.loadParentContext(servletContext); cwac.setParent(parent); } //配置和刷新上下文,也是在这里加载的配置文件 this.configureAndRefreshWebApplicationContext(cwac, servletContext); } }
我们进入这个configureAndRefreshWebApplicationContext方法查看
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) { String configLocationParam; if(ObjectUtils.identityToString(wac).equals(wac.getId())) { configLocationParam = sc.getInitParameter("contextId"); if(configLocationParam != null) { wac.setId(configLocationParam); } else { wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(sc.getContextPath())); } } wac.setServletContext(sc); /*提取之前<context-param>设置的contextConfigLocation的值*/ configLocationParam = sc.getInitParameter("contextConfigLocation"); //填充到容器属性中 if(configLocationParam != null) { wac.setConfigLocation(configLocationParam); } ConfigurableEnvironment env = wac.getEnvironment(); if(env instanceof ConfigurableWebEnvironment) { ((ConfigurableWebEnvironment)env).initPropertySources(sc, (ServletConfig)null); } this.customizeContext(sc, wac); /*刷新,也就是在这里做的对xml配置文件的加载和bean的注册,可以看之前的博客《Spring源码解析-容器功能扩展》那一章*/ wac.refresh(); }
继续回到之前的方法
/*将已经实例化的WebApplicationContext加入servletContext中*/ servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context); /*得到类加载器*/ ClassLoader ccl = Thread.currentThread().getContextClassLoader(); /*如果两个类加载器相同*/ if(ccl == ContextLoader.class.getClassLoader()) { currentContext = this.context; } else if(ccl != null) { /*如果得到的类加载器不为空,那么加入map中*/ currentContextPerThread.put(ccl, this.context); } if(logger.isDebugEnabled()) { logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" + WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]"); } if(logger.isInfoEnabled()) { long elapsedTime = System.currentTimeMillis() - startTime; logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms"); } return this.context; } catch (RuntimeException var8) { /*省略一堆异常处理*/ } } }
这就是WebApplicationContext的初始化,主要做了三件事:1、创建WebApplicationContext实例;2、加载spring的配置文件;3、将创建的实例加入到ServletContext中。
②现在来分析DispatcherServlet,DispatcherServlet实现了Servlet的接口,Servlet是用来处理处理客户端请求的一个服务者。
servlet的生命周期有三个:
(1)初始化
- servlet容器加载sevlet类
- servlet容器根据web.xml创建ServletConfig对象,该对象包含了对servlet初始化的配置信息
- servlet容器创建一个servlet对象,在创建时将ServletConfig装入对象中
- servlet容器调用servlet对象的init方法进行初始化,加载ServletConfig的配置以及其他一些操作
(2)运行阶段
servlet容器针对客户端的请求,创建servletrequest,调用对应的servlet处理,得到servletresponse对象,返回给客户端。
(3)销毁
web应用终止时,servlet容器会先调用servlet对象的destory方法,然后销毁,同时释放资源;
③那么我们来看一下DispatcherServlet这个类,首先根据生命周期servlet容器会调init方法,然而我们并没有在这里类里面找到,那么我们去父类里面找。我们在父类HttpServletBean中找到了这个方法。
public final void init() throws ServletException { if(this.logger.isDebugEnabled()) { this.logger.debug("Initializing servlet '" + this.getServletName() + "'"); } try { //解析init-param,验证,然后封装到PropertyValues中 PropertyValues pvs = new HttpServletBean.ServletConfigPropertyValues(this.getServletConfig(), this.requiredProperties); /*将这个对象转换成BeanWrapper*/ BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this); /*将servletcontext包装成资源加载器*/ ResourceLoader resourceLoader = new ServletContextResourceLoader(this.getServletContext()); /*注册自定义属性编辑器,遇到Resource类型就用resoureditor解析*/ bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.getEnvironment())); /*留给子类实现*/ this.initBeanWrapper(bw); /*将init-param的所有属性进行注入,其实就是注入到servlet中*/ bw.setPropertyValues(pvs, true); } catch (BeansException var4) { if(this.logger.isErrorEnabled()) { this.logger.error("Failed to set bean properties on servlet '" + this.getServletName() + "'", var4); } throw var4; } /*子类FrameworkServlet实现了这个方法*/ this.initServletBean(); if(this.logger.isDebugEnabled()) { this.logger.debug("Servlet '" + this.getServletName() + "' configured successfully"); } }
④接着我们找到FrameworkServlet的initServletBean方法
protected final void initServletBean() throws ServletException { this.getServletContext().log("Initializing Spring FrameworkServlet '" + this.getServletName() + "'"); if(this.logger.isInfoEnabled()) { this.logger.info("FrameworkServlet '" + this.getServletName() + "': initialization started"); } long startTime = System.currentTimeMillis(); try { //得到初始化的webApplicationContext,这个webApplicationContext跟之前监听器加载的context不一样,这主要作为springmvc的容器,那个是普通的spring容器 this.webApplicationContext = this.initWebApplicationContext(); /*让子类覆盖*/ this.initFrameworkServlet(); } catch (ServletException var5) { /*省略异常处理*/ } /*省略日志打印*/ } }
⑤接着我们来看initWebApplicationContext方法
protected WebApplicationContext initWebApplicationContext() { /*从servletContext中获取WebApplicationContext,也就是在context-param中配置的容器*/ WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext()); WebApplicationContext wac = null; /*如果this.webApplicationContext存在,不过按照这种web.xml配置文件的方式来看是不会存在的,这个servlet是tomcat进行实例化的,这个this.webApplicationContext是在构造函数中传入的,tomcat肯定不会再实例化对象时传入一个容器对象,一般是我们在取消web.xml配置,进行手动初始化时传入的。例如下面这段代码: public class WebInitializer implements WebApplicationInitializer{ public void onStartup(ServletContext servletContext) throws ServletException { AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext(); rootContext.register(ServiceConfig.class, AopConfig.class); rootContext.setServletContext(servletContext); servletContext.addListener(new ContextLoaderListener(rootContext)); AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext(); context.register(ContollerConfig.class); context.setServletContext(servletContext); ServletRegistration.Dynamic servlet = servletContext.addServlet("dispatcher",new DispatcherServlet(context)); servlet.addMapping("/"); servlet.setLoadOnStartup(1); }}*/ if(this.webApplicationContext != null) { wac = this.webApplicationContext; //如果是这个ConfigurableWebApplicationContext类型的对象 if(wac instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext)wac; //而且还存在 if(!cwac.isActive()) { if(cwac.getParent() == null) { //将在监听器中创建的容器设置为父容器 cwac.setParent(rootContext); } /*刷新上下文,也就是在这里加载spring的xml配置文件*/ this.configureAndRefreshWebApplicationContext(cwac); } } } if(wac == null) { /*根据contextAttribute属性加载context*/ wac = this.findWebApplicationContext(); }
进入findWebApplicationContext方法查看
protected WebApplicationContext findWebApplicationContext() { /*如果web.xml中servlet配置了参数contextAtrribute,那么就可以得到*/ String attrName = this.getContextAttribute(); if(attrName == null) { return null; } else { /*如果存在,从servletcontext中根据这个atrrName键获取WebApplicationContext,因为我们之前在ContextLoaderListener加载时就已经把WebApplicationContext放入了servletcontext中,不过一般不进行配置attrName是不存在的*/ WebApplicationContext wac = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext(), attrName); if(wac == null) { throw new IllegalStateException("No WebApplicationContext found: initializer not registered?"); } else { return wac; } } }
返回之前的方法
//wac不存在 if(wac == null) { //那么进行创建WebApplicationContext wac = this.createWebApplicationContext(rootContext); }
进入createWebApplicationContext方法
protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) { return this.createWebApplicationContext((ApplicationContext)parent); } 继续进入 protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) { /*获取servlet初始化参数contextClass,如果没有设置默认为XmlWebApplicationContext.class*/ Class<?> contextClass = this.getContextClass(); if(this.logger.isDebugEnabled()) { //略 if(!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) { //略 } else { //反射方式实例化 ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext)BeanUtils.instantiateClass(contextClass); wac.setEnvironment(this.getEnvironment()); //设置父容器 wac.setParent(parent); /*设置spring位置文件的路径,也就是在servlet初始参数中配置的contextConfigLocation*/ wac.setConfigLocation(this.getContextConfigLocation()); /*配置和刷新容器,也就是在这将springmvc的配置文件加载到容器中并注册bean*/ this.configureAndRefreshWebApplicationContext(wac); return wac; } }
返回之前的初始化容器方法
if(!this.refreshEventReceived) { this.onRefresh(wac); } if(this.publishContext) { String attrName = this.getServletContextAttributeName(); this.getServletContext().setAttribute(attrName, wac); } return wac; }
上面的onfresh方法其实是加载一些springmvc的组件。
我们进入这个方法查看,该方法由子类DispatcherServlet实现
protected void onRefresh(ApplicationContext context) { this.initStrategies(context); } /*我们从这个英文方法就能看出对上面组件进行初始化,初始化的过程无非是先从容器中拿到bean对象,以及一些初始化设置*/protected void initStrategies(ApplicationContext context) { this.initMultipartResolver(context); this.initLocaleResolver(context); this.initThemeResolver(context); this.initHandlerMappings(context); this.initHandlerAdapters(context); this.initHandlerExceptionResolvers(context); this.initRequestToViewNameTranslator(context); this.initViewResolvers(context); this.initFlashMapManager(context); }
到这为止,spring和springmvc的配置文件已经加载完毕了。
接下来就是要看如何springmvc对请求的处理。
我们知道tomcat服务器收到一个http请求,会去调用servlet的service方法,然后service方法再去调用doPost、doGet等方法处理。我们进入FrameworkServlet中查看service方法。
我们可以看到,如果http请求类型不是PATCH那么交给父类的service方法处理,如果是,那么就交给processRequst处理。
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { HttpMethod httpMethod = HttpMethod.resolve(request.getMethod()); if(HttpMethod.PATCH != httpMethod && httpMethod != null) { super.service(request, response); } else { this.processRequest(request, response); } }
接着进入service方法,我们可以看到对请求的处理,根据不同的请求方法类型,交给不同的方法处理,我们最常用的还是doPost和doGet
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String method = req.getMethod(); long lastModified; if(method.equals("GET")) { lastModified = this.getLastModified(req); if(lastModified == -1L) { this.doGet(req, resp); } else { long ifModifiedSince; try { ifModifiedSince = req.getDateHeader("If-Modified-Since"); } catch (IllegalArgumentException var9) { ifModifiedSince = -1L; } if(ifModifiedSince < lastModified / 1000L * 1000L) { this.maybeSetLastModified(resp, lastModified); this.doGet(req, resp); } else { resp.setStatus(304); } } } else if(method.equals("HEAD")) { lastModified = this.getLastModified(req); this.maybeSetLastModified(resp, lastModified); this.doHead(req, resp); } else if(method.equals("POST")) { this.doPost(req, resp); } else if(method.equals("PUT")) { this.doPut(req, resp); } else if(method.equals("DELETE")) { this.doDelete(req, resp); } else if(method.equals("OPTIONS")) { this.doOptions(req, resp); } else if(method.equals("TRACE")) { this.doTrace(req, resp); } else { String errMsg = lStrings.getString("http.method_not_implemented"); Object[] errArgs = new Object[]{method}; errMsg = MessageFormat.format(errMsg, errArgs); resp.sendError(501, errMsg); } }
我们查看doGet和doPost方法,可见最后都是交给了processRequest方法处理
protected final void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.processRequest(request, response); } protected final void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.processRequest(request, response); }
然后我们进入这个processRequest方法。
protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { long startTime = System.currentTimeMillis(); Throwable failureCause = null; //提取线程原来的LocalContext LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext(); //创建一个新的LocalContext LocaleContext localeContext = this.buildLocaleContext(request); //提取线程原来的RequestAttributes RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes(); //创建新的RequestAttributes ServletRequestAttributes requestAttributes = this.buildRequestAttributes(request, response, previousAttributes); WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new FrameworkServlet.RequestBindingInterceptor(null)); //将创建的localecontext和RequestAttributes和request绑定到当前线程 this.initContextHolders(request, localeContext, requestAttributes); try { //调用doService处理请求 this.doService(request, response); } catch (ServletException var17) { failureCause = var17; throw var17; } catch (IOException var18) { failureCause = var18; throw var18; } catch (Throwable var19) { failureCause = var19; throw new NestedServletException("Request processing failed", var19); } finally { //恢复线程之前的状态 this.resetContextHolders(request, previousLocaleContext, previousAttributes); if(requestAttributes != null) { requestAttributes.requestCompleted(); } /*省略日志输出*/ //发布事件通知 this.publishRequestHandledEvent(request, response, startTime, (Throwable)failureCause); } }
可以看出请求的具体处理是在doService中实现的,该方法由子类DispatcherServlet实现,查看这个方法
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception { /*省略日志输出*/ Map<String, Object> attributesSnapshot = null; if(WebUtils.isIncludeRequest(request)) { attributesSnapshot = new HashMap(); //得到所有属性的名字 Enumeration attrNames = request.getAttributeNames(); label108: while(true) { String attrName; do { if(!attrNames.hasMoreElements()) { break label108; } attrName = (String)attrNames.nextElement(); } while(!this.cleanupAfterInclude && !attrName.startsWith("org.springframework.web.servlet")); //将request中的属性装入attributesSnapshot attributesSnapshot.put(attrName, request.getAttribute(attrName)); } }/*将容器装入request属性中*/ request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.getWebApplicationContext());/*将国际化的解析器装入request属性中*/ request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver); /*将主题的解析器装入request属性中*/ request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver); /*将主题的资源装入request属性中*/ request.setAttribute(THEME_SOURCE_ATTRIBUTE, this.getThemeSource()); FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response); if(inputFlashMap != null) { request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap)); } request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap()); request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager); try { //交给doDispatch方法处理 this.doDispatch(request, response); } finally { if(!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted() && attributesSnapshot != null) { this.restoreAttributesAfterInclude(request, attributesSnapshot); } } }
可见doService并没有处理请求,只是做了一些预处理,然后叫交给了doDispatch方法,我们进入这个方法查看
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { HttpServletRequest processedRequest = request; HandlerExecutionChain mappedHandler = null; boolean multipartRequestParsed = false; WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); try { try { ModelAndView mv = null; Object dispatchException = null; try { /*如果是MultipartContext类型就将request转换成MultipartHttpServletRequest*/ processedRequest = this.checkMultipart(request); /*看processedRequest有没有变,变了就说明是Multipart类型*/ multipartRequestParsed = processedRequest != request; /*得到处理器链*/ mappedHandler = this.getHandler(processedRequest);
进入这个getHandler方法查看
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { Iterator var2 = this.handlerMappings.iterator(); //遍历所有加载的HandlerMapping HandlerExecutionChain handler; do { if(!var2.hasNext()) { return null; } HandlerMapping hm = (HandlerMapping)var2.next(); if(this.logger.isTraceEnabled()) { this.logger.trace("Testing handler map [" + hm + "] in DispatcherServlet with name '" + this.getServletName() + "'"); } //从HandlerMapping中取得Handler handler = hm.getHandler(request); } while(handler == null); return handler; }
HandlerMapping是个接口,我们需要从他的实现类中去找getHandler方法,例如我们经常用的RequestMappingHandlerMapping这个类,他的这个方法是在AbstractHandlerMapping这个父类中实现的
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { /*根据request获取handler,主要是直接匹配和通配符匹配,然后将Handler封装成HandlerExecutionChain类型,在这匹配到的是一个对应的执行方法,所以这个方法的执行还是采用了反射机制,将这个handler封装成HandlerExecutionChain的目的主要是为了后期可以添加拦截器*/ Object handler = this.getHandlerInternal(request); /*如果没找到那就使用默认的handler*/ if(handler == null) { handler = this.getDefaultHandler(); } if(handler == null) { return null; } else { //如果handler不是HandlerExecutionChain,那就是string类型,那就从容器中找到这个handler if(handler instanceof String) { String handlerName = (String)handler; handler = this.getApplicationContext().getBean(handlerName); } /*这个方法就是为了将匹配的拦截器添加到执行器链中*/ HandlerExecutionChain executionChain = this.getHandlerExecutionChain(handler, request); if(CorsUtils.isCorsRequest(request)) { CorsConfiguration globalConfig = this.corsConfigSource.getCorsConfiguration(request); CorsConfiguration handlerConfig = this.getCorsConfiguration(handler, request); CorsConfiguration config = globalConfig != null?globalConfig.combine(handlerConfig):handlerConfig; executionChain = this.getCorsHandlerExecutionChain(request, executionChain, config); } return executionChain; } }
得到处理器链后回到之前的doDispatch方法
/*如果没有找到对应的handler*/ if(mappedHandler == null || mappedHandler.getHandler() == null) { /*处理没找到信息*/ this.noHandlerFound(processedRequest, response); return; } /*找到对应的适配器*/ HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler()); /*获取方法类型*/ String method = request.getMethod(); boolean isGet = "GET".equals(method); /*如果是get或者head*/ if(isGet || "HEAD".equals(method)) { /*获取到请求的last-modidfied信息*/ long lastModified = ha.getLastModified(request, mappedHandler.getHandler()); if(this.logger.isDebugEnabled()) { this.logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified); } /*如果确认该请求的东西没有被修改,那么就直接返回,这个是http协议中的last-modified缓存机制,在客户端第一次请求该url时,服务器会在响应头中加上last-modified响应头,客户端在第二次请求该url时会向服务器发送请求头“if-modified-since”,请求头,如果服务器端内容没有变,那么返回http304状态码,减少了网络带宽*/ if((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) { return; } } /*调用拦截器方法*/ if(!mappedHandler.applyPreHandle(processedRequest, response)) { return; } /*真正的调用对应请求的方法,然后返回视图*/ mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
同样,这个HandlerAdapter是个接口,我们需要找到它的实现类,在这里我们用的是RequestMappingHandlerAdapter,然后再父类中找到了handle方法
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { return this.handleInternal(request, response, (HandlerMethod)handler); } /*进入这个方法,这个方法由子类实现*/protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { this.checkRequest(request); ModelAndView mav; if(this.synchronizeOnSession) { /*以不创建session的方式获取session,也就是说session是在getSession(true)时创建的,如果没有session就会创建,有的话就直接返回,参数为false的话,有的话直接返回,没有也不会创建session*/ HttpSession session = request.getSession(false); if(session != null) { /*如果获取到session,那么就获取到这个session中的互斥锁*/ Object mutex = WebUtils.getSessionMutex(session); /*因为这个是运行在多线程环境下的作业,session又是共享域,所有需要对session加锁*/ synchronized(mutex) { /*执行handler的方法*/ mav = this.invokeHandlerMethod(request, response, handlerMethod); } } else { /*如果没有session,那么就不会对共享变量进行访问,所以不用加锁*/ mav = this.invokeHandlerMethod(request, response, handlerMethod); } } else { mav = this.invokeHandlerMethod(request, response, handlerMethod); } /*处理Cache-Control缓存头*/ if(!response.containsHeader("Cache-Control")) { if(this.getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) { this.applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers); } else { this.prepareResponse(response); } } //返回视图 return mav; }
来看一下控制器中方法的调用,也就是invokeHandlerMethod方法
protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { /*包装成ServletWebRequest对象*/ ServletWebRequest webRequest = new ServletWebRequest(request, response); Object result; try { /*得到参数绑定工厂*/ WebDataBinderFactory binderFactory = this.getDataBinderFactory(handlerMethod); ModelFactory modelFactory = this.getModelFactory(handlerMethod, binderFactory); /*Handler方法包装成ServletInvocableHandlerMethod */ ServletInvocableHandlerMethod invocableMethod = this.createInvocableHandlerMethod(handlerMethod);/*设置方法参数解析器*/ invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);/*配置方法返回值解析器,这个返回值可能会被解析成json数据,或者视图模型等等*/ invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);/*设置数据绑定工厂*/ invocableMethod.setDataBinderFactory(binderFactory); invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer); ModelAndViewContainer mavContainer = new ModelAndViewContainer(); /*将请求中的属性添加到视图容器中*/ mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request)); modelFactory.initModel(webRequest, mavContainer, invocableMethod); mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect); /*创建异步请求*/ AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response); /*设置超时时间*/ asyncWebRequest.setTimeout(this.asyncRequestTimeout); WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);/*设置任务处理器*/ asyncManager.setTaskExecutor(this.taskExecutor); asyncManager.setAsyncWebRequest(asyncWebRequest); asyncManager.registerCallableInterceptors(this.callableInterceptors); asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors); if(asyncManager.hasConcurrentResult()) { result = asyncManager.getConcurrentResult(); mavContainer = (ModelAndViewContainer)asyncManager.getConcurrentResultContext()[0]; asyncManager.clearConcurrentResult(); if(this.logger.isDebugEnabled()) { this.logger.debug("Found concurrent result value [" + result + "]"); } invocableMethod = invocableMethod.wrapConcurrentResult(result); } /*执行方法*/ invocableMethod.invokeAndHandle(webRequest, mavContainer, new Object[0]); if(!asyncManager.isConcurrentHandlingStarted()) { ModelAndView var15 = this.getModelAndView(mavContainer, modelFactory, webRequest); return var15; } result = null; } finally { webRequest.requestCompleted(); } return (ModelAndView)result; }
返回 doDispatch方法
if(asyncManager.isConcurrentHandlingStarted()) { return; } this.applyDefaultViewName(processedRequest, mv); /*应用拦截器的后置处理器*/ mappedHandler.applyPostHandle(processedRequest, response, mv); } catch (Exception var20) { dispatchException = var20; } catch (Throwable var21) { dispatchException = new NestedServletException("Handler dispatch failed", var21); } /*对结果进行处理,包括异常的处理,以及视图的处理,异常的处理主要是调用在配置文件中定义的异常处理bean的异常处理方法,模型视图的处理主要是一些跳转,转发等问题*/ this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException); } catch (Exception var22) { this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22); } catch (Throwable var23) {/*完成处理激活触发器*/ this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23)); } } finally { if(asyncManager.isConcurrentHandlingStarted()) { if(mappedHandler != null) { mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response); } } else if(multipartRequestParsed) { this.cleanupMultipart(processedRequest); } } }
总结
DispatcherServlet这个类首先会在调用init初始化时加载springmvc的配置文件,然后将配置中配置的组件进行加载。
接着就是处理请求的功能,主要分为以下几步:
①获取要请求对应的处理器链,这里处理器链里面主要包括了拦截器和最终调用的方法;
②找到对应的处理适配器;
③执行预处理拦截方法;
④通过处理适配器调用最终执行的方法;
⑤执行后处理拦截方法;
⑥对统一抛出的异常进行处理;
⑦对返回的视图进行处理;
- Spring源码解析-springmvc
- springmvc 文件上传问题总结+spring上传文件源码解析
- springmvc源码解析(1)
- SpringMVC源码解析-ContentNegotiationStrategy
- SpringMVC源码解析-HandlerInterceptor
- SpringMVC源码解析-LocaleResolver
- spring源码-6-springmvc
- SpringMVC源码解析- HandlerAdapter初始化
- springmvc源码解析(2)
- 源码解析:SpringMVC-文件上传
- SpringMVC的DispatchServlet源码解析
- springmvc启动流程源码解析
- spring源码初步学习-SpringMVC
- Spring源码分析3----SpringMVC
- spring+springmvc+hibernate配置解析
- Spring源码解析
- spring源码解析
- spring 源码解析
- POJ1753 枚举
- easyUi—datagrid新增行保存不触发onAfterEdit事件
- MyBits 如何使用 databaseIdProvider
- 【转】计算几何题目推荐
- Java中this和super的用法总结
- Spring源码解析-springmvc
- 致橡树
- 数据结构-队列
- MyBatis 在insert插入操作时返回主键ID的配置
- 欢迎使用CSDN-markdown编辑器
- 9.15【大连24day7水题】
- 51nod 1677 treecnt(逆元求组合数+求贡献)
- WebService注解
- ffmpeg的Android.mk配置