《Spring in Action》4th Edition 学习笔记
Spring MVC 能处理从 请求 - 处理 - 返回 的所有流程,来看看它是如何工作的。
请求生命周期
- front controller也就是
DispatcherServlet
接受到请求 DispatcherServlet
根据请求url映射到对应的 controllerDispatcherServlet
发送请求到对应的 controller- controller 处理请求,把需要返回的数据放入 model 中,然后指定 view name,把包含这些数据的 request 发送回
DispatcherServlet
DispatcherServlet
生成一个 view resolver 处理逻辑视图名称- request 到达 view implementation
- 现在,view implementation 将使用 request 传入的 modal data 去渲染视图(request 的工作完成了),然后将视图写入 response object,返回给 client 端
配置 Spring MVC
使用 Servlet 3 规范,可以使用 java
来配置 servlet,而不仅仅是 xml 文件。这里主要介绍如何使用 java
配置 web 应用和 spring MVC。
package spittr.config;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
import spittr.web.WebConfig;
public class SpitterWebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[] { RootConfig.class };
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[] { WebConfig.class };
}
@Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}
Servlet 3.0 规范和 Spring DispatcherServlet 配置
在 Servlet 3.0 的环境中,容器会在 classpath 中寻找继承了javax.servlet.ServletContainerInitializer
接口的类,用它来配置 servlet 容器。
Spring 提供了一个继承这个接口的类SpringServletContainerInitializer
,在这个类中,它会寻找任何继承了WebApplicationInitializer
接口的类并用其来配置 servlet 容器。Spring 3.2 提供了一个继承了WebApplicationInitializer
接口的基类AbstractAnnotationConfigDispatcherServletInitializer
。所以,你的 servlet 配置类只需要继承AbstractAnnotation-ConfigDispatcherServletInitializer
,就会被发现而用于 servlet 容器的配置。
DispatcherServlet
VS ContextLoaderListener
在 Spring MVC 中存在两种应用上下文:DispatcherServlet
创建的和拦截器 ContextLoaderListener
创建的上下文:
DispatcherServlet
:加载包含 web 组件的 bean,比如 controllers,view resolvers 和 hanlder mappings。ContextLoaderListener
:加载其他 bean,通常是一些中间层和数据层的组件(比如数据库配置 bean 等)。
在 AbstractAnnotationConfigDispatcherServletInitializer
中 DispatcherServlet
和 ContextLoaderListener
都会被创建,而基类中的方法就可用来创建不同的应用上下文:
getServletConfigClasses()
:定义DispatcherServlet
应用上下文中的 beansgetRootConfigClasses()
:定义拦截器ContextLoaderListener
应用上下文中的 beans
Note:为了使用 AbstractAnnotationConfigDispatcherServletInitializer
必须保证 web 服务器支持 Servlet 3.0 标准(如 tomcat 7 或更高版本) 。
自定义 DispatcherServlet 配置
因为我们使用 AbstractAnnotationConfigDispatcherServletInitializer
来配置 DispatcherServlet
,所以可以通过 customizeRegistration()
方法来自定义 DispatcherServlet
。原文如下:
One such method is
customizeRegistration()
. AfterAbstractAnnotationConfigDispatcherServletInitializer
registersDispatcherServlet
with the servlet
container, it calls thecustomizeRegistration()
method, passing in theServletRegistration.Dynamic
that resulted from the servlet registration. By overriding
customizeRegistration()
, you can apply additional configuration to DispatcherServlet.
通过 ServletRegistration.Dynamic
参数配置 DispatcherServlet
的 load-on-startup 优先级 setLoadOnStartup(int loadOnStartup)
,设置初始化参数 setInitParameters()
等。具体查看文档 ServletRegistration.Dynamic。
配置额外的 servlets 和 filters
使用 java 配置 servlet 的一个好处(不同于 web.xml)就是:可以定义任意数量的初始化类。所以,如果需要定义额外的 servlets 或 filters,只需要创建额外的初始化类。在 Spring MVC 中可以通过继承 WebApplicationInitializer
接口来实现。
接下来,我们定义一个新的 servlet:
package com.myapp.config;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration.Dynamic;
import org.springframework.web.WebApplicationInitializer;
import com.myapp.MyServlet;
public class MyServletInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext throws ServletException {
Dynamic myServlet = servletContext.addServlet("myServlet", MyServlet.class);
myServlet.addMapping("/custom/**");
}
}
当然,你也可以用来定义 filters 和 listeners:
@Override
public void onStartup(ServletContext servletContext)
throws ServletException {
// 定义filter
javax.servlet.FilterRegistration.Dynamic filter =
servletContext.addFilter("myFilter", MyFilter.class);
filter.addMappingForUrlPatterns(null, false, "/custom/*");
}
如果你需要为 DispatcherServlet
添加 filter 的话,就不用这么麻烦了,你只要重写 AbstractAnnotationConfigDispatcherServletInitializer
类的 getServletFilters()
方法就行了:
@Override
protected Filter[] getServletFilters() {
return new Filter[] { new MyFilter() };
}
不需要 mapping,因为会自动 mapping 到 DispatcherServlet
上,通过返回多个 filter,可以添加多个 filter。
开启 Spring MVC 支持
Spring 使用如下方法开启 MVC 的支持:
@EnableWebMvc
注解(JavaConfig):和@Configuration
注解一起使用<mvc:annotation-driven />
元素(XML 配置)
开启 MVC 支持,它会从 WebMvcConfigurationSupport
导入 Spring MVC 的配置,会在处理请求时加入注解的支持(比如 @RequestMapping
,@ExceptionHandler
等注解)。
如果需要自定义配置,从 @EnableWebMvc
的文档上来看,需要继承 @WebMvcConfigurer
接口或者继承基类 WebMvcConfigurerAdapter
(它继承了 @WebMvcConfigurer
接口,但是用空方法实现)。所以,覆盖相应的方法就能实现 mvc 配置的自定义。
那么,我们需要在 web mvc 配置中做哪些事情呢:
- 开启 ComponentScan
- View Resolver(视图解析)
- 静态文件处理
View Resolver 将在后面介绍,这里先讨论如何处理静态文件(html, css, js)
静态文件处理
Spring 可以有两种方式处理静态文件:
- 转发到默认的 web 服务器的 servlet 处理(比如 tomcat 来处理)
- 使用 Spring ResourceHandler 处理
使用这两种办法都需要继承 WebMvcConfigurerAdapter
基类,覆盖其中相应的方法实现。
默认 Servlet 处理
@Override
public