SpringMVC在JavaWeb中应用很广,处理前台、接请求并返回页面、数据,本文尝试理解SpringMVC在web应用中的工作流程。

假定我们建立测试项目WGS、使用Tomcat8.0作为我们的调试服务器,我们将web应用程序WGS部署到Tomcat的webapps目录,如图:

Tomcat目录下有其内置的其它应用模块,每个应用的下的目录结构相似,下面是WGS的应用结构

WEB-INF下是应用程序的核心代码,其中web.xml文件是整个Web应用的部署文件和工程加载文件(web.xml不是Web应用必须的配置文件,但在基于jsp开发的动态网站应用中,web.xml几乎无处不在)。

其实Tomcat的配置文件夹下也有web.xml,这是配置所有Web应用的公共配置,每个Web应用拥有自己的web.xml进行个性化配置。

web.xml of Tomcat


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
83
84
85
86
87
88
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.1" 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">

<!-- The default servlet for all web applications, that serves static -->
<!-- resources. It processes all requests that are not mapped to other -->
<!-- servlets with servlet mappings (defined either here or in your own -->
<!-- web.xml file). This servlet supports the following initialization -->
<!-- parameters (default values are in square brackets): -->
<servlet>
<servlet-name>default</servlet-name>
<servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>listings</param-name>
<param-value>false</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- The mapping for the default servlet -->
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>

<!-- The JSP page compiler and execution servlet, which is the mechanism -->
<!-- used by Tomcat to support JSP pages. Traditionally, this servlet -->
<!-- is mapped to the URL pattern "*.jsp". This servlet supports the -->
<!-- following initialization parameters (default values are in square -->
<!-- brackets): -->
<servlet>
<servlet-name>jsp</servlet-name>
<servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
<init-param>
<param-name>fork</param-name>
<param-value>false</param-value>
</init-param>
<init-param>
<param-name>xpoweredBy</param-name>
<param-value>false</param-value>
</init-param>
<load-on-startup>3</load-on-startup>
</servlet>
<!-- The mappings for the JSP servlet -->
<servlet-mapping>
<servlet-name>jsp</servlet-name>
<url-pattern>*.jsp</url-pattern>
<url-pattern>*.jspx</url-pattern>
</servlet-mapping>

<!-- ==================== Default Session Configuration ================= -->
<!-- You can set the default session timeout (in minutes) for all newly -->
<!-- created sessions by modifying the value below. -->
<session-config>
<session-timeout>30</session-timeout>
</session-config>

<!-- ===================== Default MIME Type Mappings =================== -->
<!-- When serving static resources, Tomcat will automatically generate -->
<!-- a "Content-Type" header based on the resource's filename extension, -->
<!-- based on these mappings. Additional mappings can be added here (to -->
<!-- apply to all web applications), or in your own application's web.xml -->
<!-- deployment descriptor. -->
<!-- Note: Extensions are always matched in a case-insensitive manner. -->
<mime-mapping>
<extension>zmm</extension>
<mime-type>application/vnd.handheld-entertainment+xml</mime-type>
</mime-mapping>
.
.
.
<mime-mapping>
<extension>123</extension>
<mime-type>application/vnd.lotus-1-2-3</mime-type>
</mime-mapping>

<!-- ==================== Default Welcome File List ===================== -->
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>

</web-app>
  • Servlet default
    默认Servlet,/ 过滤所有Web应用的请求,可以处理所有应用的静态资源,也处理在其他应用中未匹配到请求,所以优先级比较低。

  • Servlet jsp
    JspServlet,*.jsp处理.jsp后缀文件,编译、执行JSP,浏览器不能显示动态网页(基于动态网站技术的页面,e.g jsp,php,.net),JspServlet将jsp编译后生成html,返回给客户端。

  • session-timeout
    session的存活时间。

  • mime-mapping
    mine-mapping通过匹配文件后缀判断mine类型,123即为文件扩展名,服务器通过mine-mapping告诉浏览器返回内容的mine类型。

  • Welcome List
    welcome-file-list配置Web应用的欢迎页,如果Web应用没有在自己的web.xml中配置此项,index.html指定其默认页面为index.html。

注:动态网页不是指有动态效果的页面,而是基于动态网站技术开发的页面,如jsp,php,asp等,动态网页和静态网页可通过下图来区分。

web.xml of WGS


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
83
84
85
86
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">

<display-name>WGS</display-name>

<!-- Context Param -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath*:/applicationContext.xml,
classpath*:/applicationContext-shiro.xml
</param-value>
</context-param>
<context-param>
<param-name>spring.profiles.default</param-name>
<param-value>production</param-value>
</context-param>

<!-- Context Loader Listener -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<!-- Encoding Config -->
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

<!-- JPA Config -->
<filter>
<filter-name>openEntityManagerInViewFilter</filter-name>
<filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>openEntityManagerInViewFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

<!-- Shiro Config -->
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>targetFilterLifecycle</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

<!-- Spring MVC Config -->
<servlet>
<servlet-name>springServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>

<error-page>
<error-code>404</error-code>
<location>/WEB-INF/views/error/404.jsp</location>
</error-page>

</web-app>

如上,Web应用web.xml中主要有几个标签,context-param、listener、filter、filter-mapping、servlet、servlet-mapping,Tomcat初始化顺序为context-param、listener、filter、servlet,初始化流程如下:

  • Tomcat读取web.xml中的context-param、listener两类节点
  • Tomcat创建ServletContext,即上Web应用下文,将context-param对应的键值交给ServletContext,接着创建listener
  • Tomcat实例化listener对应的类(ContextLoaderListener),根据listener中的方法加载context-param的值,也就是去加载applicationContext.xml和applicationContext-shiro.xml,以及根据spring.profiles.default加载生产环境下的属性文件。这些配置文件描述了如何加载应用程序。
  • Tomcat初始化过滤器Filter
  • Tomcat初始化Servlet,servlet-mapping定义过滤规则,load-on-startup指定启动顺序,大于1则此Servlet启动优先级最高。

springServlet就是应用程序的Servlet,使用的是org.springframework.web.servlet.DispatcherServlet这个类,Tomcat实例化此类来处理web请求,具体实例化配置在 /WEB-INF/spring-mvc.xml 中定义,此配置文件如下:

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
<?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:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd">

<!-- 自动扫描且只扫描 @Controller -->
<context:component-scan base-package="com.smarts"
use-default-filters="false">
<context:include-filter type="annotation"
expression="org.springframework.stereotype.Controller" />
<context:include-filter type="annotation"
expression="org.springframework.web.bind.annotation.ControllerAdvice" />
</context:component-scan>

<mvc:annotation-driven>
<mvc:message-converters register-defaults="true">
<!-- 将StringHttpMessageConverter的默认编码设为UTF-8 -->
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<constructor-arg value="UTF-8" />
</bean>
</mvc:message-converters>
</mvc:annotation-driven>

<!-- 定义JSP文件的位置 -->
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/" />
<property name="suffix" value=".jsp" />
</bean>

<!-- 容器默认的DefaultServletHandler处理 所有静态内容与无RequestMapping处理的URL -->
<mvc:default-servlet-handler />

<!-- 定义无需Controller的url<->view直接映射 -->
<mvc:view-controller path="/" view-name="redirect:/manage/" />

<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="defaultEncoding" value="utf-8"></property>
</bean>

<!-- 替换异常处理方式。指定的异常转到指定页面;否则作为json返回 -->
<bean id="exceptionResolver"
class="com.smarts.exception.CustomSimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<prop key="com.smarts.exception.WebException">error/jsonerror</prop>
<prop key="java.lang.NullPointerException">error/jsonerror</prop>
<prop key="java.lang.exception">error/500</prop>

</props>
</property>
</bean>
</beans>

如上,是我们熟悉的SpringMVC的配置文件,通过@Controller注解来管理请求处理类,并定义返回jsp文件的位置。

(完)