Spring MVC-注解开发-初始化过程
前置文章:
Spring MVC-简单使用
零、本文纲要
一、快速入门
二、初始化过程
- Servlet规范
1.1 补充:POST请求乱码处理 - 初始化过程分析
一、快速入门
1. 基础依赖
① spring-context
② spring-webmvc
③ javax.servlet-api
2. 配置类
① SpringConfig
② SpringMvcConfig
③ ServletContainersInitConfig
二、初始化过程
1. Servlet规范
- ① ServletContainerInitializer
ServletContainerInitializer 是 Servlet 3.0 新增的一个接口,主要用于在容器启动阶段通过编程风格注册Filter, Servlet以及Listener,以取代通过web.xml配置注册。
Servlet 3.0 规范中,容器启动阶段会读取项目的/META-INF/services/javax.servlet.ServletContainerInitializer
文件,进行初始化准备工作。
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
...
}
Ⅰ @HandlesTypes(WebApplicationInitializer.class)
作用:是将注解指定的Class对象作为参数传递到ServletContainerInitializer#onStartup
方法中。
Ⅱ WebApplicationInitializer
该接口我们需要实现,以将实现类传递给ServletContainerInitializer#onStartup
方法。
public interface WebApplicationInitializer {
void onStartup(ServletContext servletContext) throws ServletException;
}
所以我们MVC项目中需要编写该WebApplicationInitializer接口的实现类,来进行一些自定义的配置。
Ⅲ AbstractDispatcherServletInitializer
常常我们会继承该抽象类,其也是WebApplicationInitializer接口的抽象实现类。
DispatcherServlet是我们熟悉的Spring MVC项目中的前端控制器。
public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer {
//加载springmvc配置类
protected WebApplicationContext createServletApplicationContext() {
//初始化WebApplicationContext对象
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
//加载指定配置类
ctx.register(SpringMvcConfig.class);
return ctx;
}
//设置由springmvc控制器处理的请求映射路径
protected String[] getServletMappings() {
return new String[]{"/"}; // 表示所有请求路径由springmvc控制器处理
}
//加载spring配置类
protected WebApplicationContext createRootApplicationContext() {
//初始化WebApplicationContext对象
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
//加载指定配置类
ctx.register(SpringConfig.class);
return ctx;
}
}
注意:如果写成WebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
,则无法调用子类的register方法。
1.1 补充:POST请求乱码处理
- ① 方式一:重写getServletFilters方法(推荐)
//POST请求乱码处理
@Override
protected Filter[] getServletFilters() {
CharacterEncodingFilter filter = new CharacterEncodingFilter();
filter.setEncoding("UTF-8");
return new Filter[]{filter};
}
- ② 方式二:重写onStartup方法(5.1.6版本及之前有此方法)
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
//触发父类的onStartup
super.onStartup(servletContext);
//1.创建字符集过滤器对象
CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
//2.设置使用的字符集
characterEncodingFilter.setEncoding("UTF-8");
//3.添加到容器(它不是IOC容器,而是ServletContainer)
FilterRegistration.Dynamic registration = servletContext.addFilter("characterEncodingFilter",characterEncodingFilter);
}
2. 初始化过程分析
- ① SpringServletContainerInitializer#onStartup
for (WebApplicationInitializer initializer : initializers) {
//【断点】
initializer.onStartup(servletContext);
}
- ② AbstractDispatcherServletInitializer#onStartup
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
//【断点】此处会完成Spring配置类的注册
super.onStartup(servletContext);
//【断点】此处会往ServletContext中注册前端控制器DispatcherServlet
registerDispatcherServlet(servletContext);
}
- ③ AbstractContextLoaderInitializer#onStartup
【重点】此处会完成RootApplicationContext父容器的初始化。
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
//【断点】
registerContextLoaderListener(servletContext);
}
protected void registerContextLoaderListener(ServletContext servletContext) {
//【断点】调用我们编写的ServletContainersInitConfig配置类的createRootApplicationContext方法
WebApplicationContext rootAppContext = createRootApplicationContext();
if (rootAppContext != null) {
//把ContextLoaderListener添加到ServletContext中
ContextLoaderListener listener = new ContextLoaderListener(rootAppContext);
listener.setContextInitializers(getRootApplicationContextInitializers());
servletContext.addListener(listener);
}
else {
logger.debug("No ContextLoaderListener registered, as " +
"createRootApplicationContext() did not return an application context");
}
}
我们编写的ServletContainersInitConfig继承了AbstractDispatcherServletInitializer,而且AbstractDispatcherServletInitializer继承了AbstractContextLoaderInitializer。
所以,此处调用的createRootApplicationContext方法,实际上就是我们ServletContainersInitConfig配置类内部重写的方法。
- ④ AbstractDispatcherServletInitializer#registerDispatcherServlet
【重点】此处会完成ServletApplicationContext子容器的初始化,并且添加映射规则,添加自定义过滤器。
protected void registerDispatcherServlet(ServletContext servletContext) {
String servletName = getServletName();
Assert.hasLength(servletName, "getServletName() must not return null or empty");
//【断点】调用我们编写的ServletContainersInitConfig配置类的createServletApplicationContext方法
WebApplicationContext servletAppContext = createServletApplicationContext();
Assert.notNull(servletAppContext, "createServletApplicationContext() must not return null");
FrameworkServlet dispatcherServlet = createDispatcherServlet(servletAppContext);
Assert.notNull(dispatcherServlet, "createDispatcherServlet(WebApplicationContext) must not return null");
dispatcherServlet.setContextInitializers(getServletApplicationContextInitializers());
//往servletContext容器中添加前端控制器dispatcherServlet
ServletRegistration.Dynamic registration = servletContext.addServlet(servletName, dispatcherServlet);
if (registration == null) {
throw new IllegalStateException("Failed to register servlet with name '" + servletName + "'. " +
"Check if there is another servlet registered under the same name.");
}
//设置加载时机
registration.setLoadOnStartup(1);
//【断点】添加映射,调用我们编写的ServletContainersInitConfig配置类的getServletMappings方法
registration.addMapping(getServletMappings());
registration.setAsyncSupported(isAsyncSupported());
//【断点】添加过滤器,调用我们编写的ServletContainersInitConfig配置类的getServletFilters方法;如果没有重写,则返回null
Filter[] filters = getServletFilters();
if (!ObjectUtils.isEmpty(filters)) {
for (Filter filter : filters) {
registerServletFilter(servletContext, filter);
}
}
customizeRegistration(registration);
}
三、结尾
以上即为Spring MVC-注解开发-初始化过程的基础内容,感谢阅读。