0%

SpringMVC配置及源码分析

一、环境搭建

  1. 创建一个web项目。
  • 如果是maven项目,则直接在pom中加入springMvc依赖
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <dependencies>
    <dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>3.1.0</version>
    </dependency>
    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>4.1.9.RELEASE</version>
    </dependency>
    </dependencies>

如果不是就从把这些jar包丢到lib里面

二、SpringMVC配置

  1. 在web中配置servlet,
    url-pattern中的/ 和 / * 有区别
    / 不会匹配到*.jsp,即:*.jsp不会进入spring的 DispatcherServlet类 。
    /* 会匹配*.jsp,会出现返回jsp视图时再次进入spring的DispatcherServlet 类,导致找不到对应的controller所以报404错。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app version="2.4"
    xmlns="http://java.sun.com/xml/ns/j2ee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
    http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
    <!--SpringMvc开始-->
    <servlet>
    <servlet-name>springMvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    </servlet>
    <servlet-mapping>
    <servlet-name>springMvc</servlet-name>
    <url-pattern>/</url-pattern>
    </servlet-mapping>
    <!--SpringMvc结束-->
    </web-app>
  • 创建springMvc-servlet.xml文件,放到WEB-INF路径下
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    <?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/tool http://www.springframework.org/schema/tool/spring-tool.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="com.tianzeng"/>
    <!--通用视图解析器 -->
    <bean id="viewResolverCommon"
    class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/views" />
    <property name="suffix" value=".jsp" />
    </bean>
    </beans>

三、测试

创建Controller:

1
2
3
4
5
6
7
8
9
10
11
12
13
@Controller
public class HelloWorld {
@ResponseBody
@RequestMapping("/helloworld")
public String helloWorld(){
return "Hello world";
}
@RequestMapping("/hello")
public String hello(Model model){
model.addAttribute("msg","hello");
return "/hello";
}
}

创建资源文件:

1
2
3
4
5
6
7
8
9
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
${msg}
</body>
</html>

然后把项目丢到tomcat中启动,访问/helloworld就能够看见Hello world了,访问/hello能够看到hello

四、源码分析

springMvc 核心 servlet 结构图
SpringMVC的servlet的有三个层次:分别是HttpServletBean、FrameworkServlet、DispatcherServlet。

初始化

  1. HttpServletBean初始化
    HttpServletBean直接继承了java的HttpServlet,创建时自动调用init方法进行初始化

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
       @Override
    public final void init() throws ServletException {
    if (logger.isDebugEnabled()) {
    logger.debug("Initializing servlet '" + getServletName() + "'");
    }
    // Set bean properties from init parameters.
    try {
    PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
    // 使用BeanWrapper构造DispatcherServlet
    BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
    ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
    bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
    initBeanWrapper(bw);
    bw.setPropertyValues(pvs, true); // 设置DispatcherServlet属性
    }
    catch (BeansException ex) {
    logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
    throw ex;
    }
    // 子类覆盖此方法,一起给初始化了
    initServletBean();

    if (logger.isDebugEnabled()) {
    logger.debug("Servlet '" + getServletName() + "' configured successfully");
    }
    }
  2. FrameworkServlet初始化
    FrameworkServlet是HttpServletBean类的子类,所以初始化操作是覆盖initServletBean

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
       @Override
    protected final void initServletBean() throws ServletException {
    getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
    if (this.logger.isInfoEnabled()) {
    this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");
    }
    long startTime = System.currentTimeMillis();

    try {
    // initServletBean的主要方法,初始化webApplicationContext
    this.webApplicationContext = initWebApplicationContext();
    // 这个里面没有任何实现方法
    initFrameworkServlet();
    }
    catch (ServletException ex) {
    this.logger.error("Context initialization failed", ex);
    throw ex;
    }
    catch (RuntimeException ex) {
    this.logger.error("Context initialization failed", ex);
    throw ex;
    }

    if (this.logger.isInfoEnabled()) {
    long elapsedTime = System.currentTimeMillis() - startTime;
    this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +
    elapsedTime + " ms");
    }
    }
    protected WebApplicationContext initWebApplicationContext() {
    WebApplicationContext rootContext =
    WebApplicationContextUtils.getWebApplicationContext(getServletContext()); // 得到根上下文
    WebApplicationContext wac = null;
    if (this.webApplicationContext != null) {
    wac = this.webApplicationContext;
    if (wac instanceof ConfigurableWebApplicationContext) {
    ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
    if (!cwac.isActive()) {
    if (cwac.getParent() == null) {
    cwac.setParent(rootContext);
    }
    configureAndRefreshWebApplicationContext(cwac);
    }
    }
    }
    if (wac == null) {
    wac = findWebApplicationContext();
    }
    if (wac == null) {
    wac = createWebApplicationContext(rootContext); // 创建一个WebApplicationContext
    }

    if (!this.refreshEventReceived) {
    onRefresh(wac); // 模板方法,子类DispatcherServlet会覆盖这个方法进行初始化
    }

    if (this.publishContext) {
    String attrName = getServletContextAttributeName();
    getServletContext().setAttribute(attrName, wac); // 将新创建的容器设置到ServletContext中去
    if (this.logger.isDebugEnabled()) {
    this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
    "' as ServletContext attribute with name [" + attrName + "]");
    }
    }

    return wac;
    }
  3. DispatcherServlet初始化

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
       @Override
    protected void onRefresh(ApplicationContext context) {
    initStrategies(context);
    }
    protected void initStrategies(ApplicationContext context) {
    //初始化多媒体解析器
    initMultipartResolver(context);
    //初始化位置解析器
    initLocaleResolver(context);
    //初始化主题解析器
    initThemeResolver(context);
    //初始化HandlerMappings
    initHandlerMappings(context);
    // 初始化HandlerAdapters
    initHandlerAdapters(context);
    //初始化异常解析器
    initHandlerExceptionResolvers(context);
    //初始化请求到视图名转换器
    initRequestToViewNameTranslator(context);
    //初始化视图解析器
    initViewResolvers(context);
    //初始化FlashMapManager
    initFlashMapManager(context);
    }

请求处理

  1. HttpServletBean请求处理
    HttpServletBean没有进行任何请求处理,只是参与了容器的初始化操作
  2. FrameworkServlet请求处理
    service方法是HttpServlet中的方法,servlet容器把所有请求发送到该方法,该方法默认行为是转发http请求到doXXX方法中,如果重载了该方法,默认操作被覆盖,不再进行转发操作
    FrameworkServlet重写了service方法,如果Http请求为PATCH则使用processRequest处理,否则使用父类的service去处理
    但是除了doOptions、doTrace这两个方法FrameworkServlet用了自己的实现,其他的处理最后都使用的是processRequest
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
       @Override
    protected void service(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {

    if (RequestMethod.PATCH.name().equalsIgnoreCase(request.getMethod())) {
    processRequest(request, response);
    }
    else {
    super.service(request, response);
    }
    }

processRequest是FrameworkServlet这个类中最核心的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
   protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {

long startTime = System.currentTimeMillis();
Throwable failureCause = null;

LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
LocaleContext localeContext = buildLocaleContext(request);

RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);

WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());

initContextHolders(request, localeContext, requestAttributes);
// 上面一大堆都是用于初始化一些乱七八糟的东东,下面的才是处理请求的
try {
// 模板方法,交由子类DispatcherServlet去处理
doService(request, response);
}
catch (ServletException ex) {
failureCause = ex;
throw ex;
}
catch (IOException ex) {
failureCause = ex;
throw ex;
}
catch (Throwable ex) {
failureCause = ex;
throw new NestedServletException("Request processing failed", ex);
}

finally {
resetContextHolders(request, previousLocaleContext, previousAttributes);
if (requestAttributes != null) {
requestAttributes.requestCompleted();
}

if (logger.isDebugEnabled()) {
if (failureCause != null) {
this.logger.debug("Could not complete request", failureCause);
}
else {
if (asyncManager.isConcurrentHandlingStarted()) {
logger.debug("Leaving response open for concurrent processing");
}
else {
this.logger.debug("Successfully completed request");
}
}
}

publishRequestHandledEvent(request, response, startTime, failureCause);
}
}

  1. DispatcherServlet请求处理

DispatcherServlet对于请求分为两步,第一步是请求处理,第二步是渲染页面
如果请求进来,会调用DispatcherServlet的doService方法去进行处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
   @Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
if (logger.isDebugEnabled()) {
String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";
logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed +
" processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");
}

Map<String, Object> attributesSnapshot = null;
if (WebUtils.isIncludeRequest(request)) {
attributesSnapshot = new HashMap<String, Object>();
Enumeration<?> attrNames = request.getAttributeNames();
while (attrNames.hasMoreElements()) {
String attrName = (String) attrNames.nextElement();
if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) {
attributesSnapshot.put(attrName, request.getAttribute(attrName));
}
}
}
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
request.setAttribute(THEME_SOURCE_ATTRIBUTE, 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);
// 上面对请求进行了一些处理,doDispatch才是去处理请求的方法
try {
doDispatch(request, response);
}
finally {
if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
if (attributesSnapshot != null) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
   protected 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);

// 去找对应的请求处理器
mappedHandler = getHandler(processedRequest);
// 如果找不到对应的请求处理器则直接返回404错误
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
// 根据请求处理器,获取执行操作的请求适配器
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

// 获取请求的方式
String method = request.getMethod();
boolean isGet = "GET".equals(method);
// head请求和get一樣,只是head只会取的HTTP header的信息。
if (isGet || "HEAD".equals(method)) {
// lastModified 属性可返回文档最后被修改的日期和时间。
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (logger.isDebugEnabled()) {
logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
}
// checkNotModified逻辑判断当前lastModfied值和http header的上次缓存值,如果还没有过期就设置304头并且返回并结束整个请求流程。否则继续。
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}

// 实际上执行处理的方法,通过请求访问对应的处理器,并且返回modelandview对象
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// 如果是异步请求直接返回
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
// 设置默认的视图
applyDefaultViewName(request, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
// 处理返回结果。包括异常处理、渲染页面、发成完成通知出发Interceptor的afterCompletion
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()) {
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// 删除上传请求的资源
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}