SpringMVC深入理解

核心类与接口

- DispatcherServlet 前置控制器
- HandlerMapping 请求映射(到Controller)
- HandlerAdapter 请求映射(到Controller类的方法上)
- Controller 控制器
- HandlerIntercepter 拦截器
- ViewResolver 视图映射
- View 视图处理

启动过程

Spring MVC启动过程大致分为两个过程:
- ContextLoaderListener初始化,读取context-param中的contextConfigLocation指定的配置文件,创建ROOT Context,通过调用继承自ContextLoader的initWebApplicationContext方法实例化Spring IoC容器,并将此容器实例注册到ServletContext中
- ContextLoaderListener初始化完毕后,开始初始化web.xml中配置的DispatcherServlet,DispatcherServlet的初始化又包括了视图管理器、异常处理器、映射管理等等;

    /**
* Initialize the strategy objects that this servlet uses.
* <p>May be overridden in subclasses in order to initialize further strategy objects.
*/
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}

ContextLoaderListener初始化的是 WebApplicationContext, 创建后可以从ServletContext中获取,WebApplicationContext是应用程序内共享的,最多只有一个,如果寻求简单也可以不初始化此容器。与之不同 DispatcherServlet可以有多个,并共享一个WebApplicationContext容器,每一个DispatcherServlet有不同的配置,控制不同的WEB访问。一般将 DAO、Service 层Bean共享的放在ContextLoaderListener配置的容器中,将WEB层的Bean放在特定的DispatcherServlet配置的容器中。

SpringMVC利用Spring的注入特性初始化资源文件,只需要调用setPropertyValues方法就可将contextConfigLocation属性设置到对应实例中,也就是以依赖注入的方式初始化属性。

时序图如下(盗图):
SpringMVC深入理解

请求处理流程

官网上的图
SpringMVC深入理解

涉及到核心类与接口的过程描述:

客户端浏览器发送http请求,被`DispatcherServlet`捕获,调用关键的doDispatch方法,遍历所有注册为`Controller`的bean,为请求寻找关联映射,其中遍历查找的函数getHandler和getHandlerAdapter的源码:

 /**
* Return the HandlerExecutionChain for this request.
* <p>Tries all handler mappings in order.
* @param request current HTTP request
* @return the HandlerExecutionChain, or {@code null} if no handler could be found
*/
protected 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;
} /**
* Return the HandlerAdapter for this handler object.
* @param handler the handler object to find an adapter for
* @throws ServletException if no HandlerAdapter can be found for the handler. This is a fatal error.
*/
protected 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`会依次调用preHandle、handle(返回ModelAndView)、postHandle方法,所有步骤完成后调用processDispatchResult函数处理结果,并返回View给客户端。postDispatchResult函数和其中调用的render函数源码如下:

 /**
* Handle the result of handler selection and handler invocation, which is
* either a ModelAndView or an Exception to be resolved to a ModelAndView.
*/
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception { boolean errorView = false; if (exception != null) {
if (exception instanceof ModelAndViewDefiningException) {
logger.debug("ModelAndViewDefiningException encountered", exception);
mv = ((ModelAndViewDefiningException) exception).getModelAndView();
}
else {
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
mv = processHandlerException(request, response, handler, exception);
errorView = (mv != null);
}
} // Did the handler return a view to render?
if (mv != null && !mv.wasCleared()) {
render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
"': assuming HandlerAdapter completed request handling");
}
} if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Concurrent handling started during a forward
return;
} if (mappedHandler != null) {
mappedHandler.triggerAfterCompletion(request, response, null);
}
} /**
* Render the given ModelAndView.
* <p>This is the last stage in handling a request. It may involve resolving the view by name.
* @param mv the ModelAndView to render
* @param request current HTTP servlet request
* @param response current HTTP servlet response
* @throws ServletException if view is missing or cannot be resolved
* @throws Exception if there's a problem rendering the view
*/
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
// Determine locale for request and apply it to the response.
Locale locale = this.localeResolver.resolveLocale(request);
response.setLocale(locale); View view;
if (mv.isReference()) {
// We need to resolve the view name.
view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
if (view == null) {
throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
"' in servlet with name '" + getServletName() + "'");
}
}
else {
// No need to lookup: the ModelAndView object contains the actual View object.
view = mv.getView();
if (view == null) {
throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
"View object in servlet with name '" + getServletName() + "'");
}
} // Delegate to the View object for rendering.
if (logger.isDebugEnabled()) {
logger.debug("Rendering view [" + view + "] in DispatcherServlet with name '" + getServletName() + "'");
}
try {
if (mv.getStatus() != null) {
response.setStatus(mv.getStatus().value());
}
view.render(mv.getModelInternal(), request, response);
}
catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug("Error rendering view [" + view + "] in DispatcherServlet with name '" +
getServletName() + "'", ex);
}
throw ex;
}
}

这就是一个完整的处理http请求的过程。盗图一张:
SpringMVC深入理解

时序图如下(来源:http://neoremind.com/2016/02/springmvc%E7%9A%84%E4%B8%80%E4%BA%9B%E5%B8%B8%E7%94%A8%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5/):
SpringMVC深入理解

配置实例

这里放的是最简单的配置,可以通过这个简单的配置实例回顾一下上面的过程。

目录结构

-SpringMVCDemo
        -src
            -me.cyan
                -WelcomeController
        -web
            -WEB-INF
                -applicationContext.xml
                -dispatcher-servlet.xml
                -web.xml
            -index.jsp
        -pom.xml
        
pom.xml
引入的包

 <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>me.cyan</groupId>
<artifactId>SpringMVCDemo</artifactId>
<name>SpringMVCDemo</name>
<packaging>war</packaging>
<version>1.0.0</version> <properties>
<spring-version>4.2.6.RELEASE</spring-version>
</properties> <dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring-version}</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>${spring-version}</version>
</dependency>
</dependencies>
</project>

web.xml

 <?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1"> <!--配置文件路径-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener> <!--SpringMVC核心servlet-->
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>

dispatcher-servlet.xml

 <?xml version="1.0" encoding="UTF-8"?>
<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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 默认的注解映射的支持 -->
<mvc:annotation-driven /> <!-- 自动扫描的包名 -->
<context:component-scan base-package="me.cyan" /> </beans>

WelcomeController

 package me.cyan;

 import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody; /**
* Created by cyan on 16/5/23.
*/ @Controller
public class welcomeController { @RequestMapping("/hello")
public @ResponseBody String sayhello(){
return "hello Spring MVC!";
}
}

运行结果
SpringMVC深入理解

上一篇:Python+Selenium 自动化实现实例-定位一组对象(checkbox,inputs)


下一篇:mysql 权限设置