Spring支持Servlet3.0
由上一节可以知道,利用Servlet3.0的新特性,我们在编写Servlet的时候可以不使用xml配置文件了,直接使用注解就可以了。本节将会介绍Spring是如何支持Servlet3.0。
首先导入SpringMVC的依赖
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.14.RELEASE</version>
</dependency>
</dependencies>
查看spring-web-5.1.14.RELEASE依赖目录下的/META-INF/services/javax.servlet.ServletContainerInitializer文件,文件内容如下
org.springframework.web.SpringServletContainerInitializer
因此我们可以知道,容器在启动时将会调用这个类的#onStartup(...)
方法。
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
@Override
public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
throws ServletException {
List<WebApplicationInitializer> initializers = new LinkedList<>();
if (webAppInitializerClasses != null) {
for (Class<?> waiClass : webAppInitializerClasses) {
// 如果waiClass不是一个接口、不是一个抽象类、并且是WebApplicationInitializer类的子类,则实例化并加到initializers
if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
try {
initializers.add((WebApplicationInitializer)
ReflectionUtils.accessibleConstructor(waiClass).newInstance());
}
catch (Throwable ex) {
throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
}
}
}
}
if (initializers.isEmpty()) {
servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
return;
}
servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
AnnotationAwareOrderComparator.sort(initializers);
// initializers 列表并执行onStartup方法
for (WebApplicationInitializer initializer : initializers) {
initializer.onStartup(servletContext);
}
}
}
代码比较简单,利用@HandlesTypes(WebApplicationInitializer.class)
注解,收集所有不是接口、不是抽象类并且是WebApplicationInitializer
接口的子类,最后调用#onStartup(ServletContext servletContext)
方法。
WebApplicationInitializer
有三个子类,分别是AbstractAnnotationConfigDispatcherServletInitializer
、AbstractDispatcherServletInitializer
、AbstractContextLoaderInitializer
这三个类的关键代码如下所示
AbstractAnnotationConfigDispatcherServletInitializer
// SpringMVC里面有两个容器,
// 一个是父容器=》存储普通Bean,如Service、Component等
// 一个是子容器=》存储Controller相关Bean
// AbstractAnnotationConfigDispatcherServletInitializer.java
public abstract class AbstractAnnotationConfigDispatcherServletInitializer
extends AbstractDispatcherServletInitializer {
// 创建一个父容器, Root WebApplicationContext
@Override
@Nullable
protected WebApplicationContext createRootApplicationContext() {
Class<?>[] configClasses = getRootConfigClasses();
if (!ObjectUtils.isEmpty(configClasses)) {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(configClasses);
return context;
}
else {
return null;
}
}
// 创建一个子容器, Servlet WebApplicationContext
@Override
protected WebApplicationContext createServletApplicationContext() {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
Class<?>[] configClasses = getServletConfigClasses();
if (!ObjectUtils.isEmpty(configClasses)) {
context.register(configClasses);
}
return context;
}
// 指定父容器的配置类, 由子类实现
@Nullable
protected abstract Class<?>[] getRootConfigClasses();
// 指定子容器的配置类, 由子类实现
@Nullable
protected abstract Class<?>[] getServletConfigClasses();
}
AbstractDispatcherServletInitializer
// AbstractDispatcherServletInitializer.java
public abstract class AbstractDispatcherServletInitializer extends AbstractContextLoaderInitializer {
// 默认的Servlet名字
public static final String DEFAULT_SERVLET_NAME = "dispatcher";
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
// 调用父类的onStartup方法, 这里会新建父容器, Root WebApplicationContext
super.onStartup(servletContext);
// 注册DispatcherServlet, 这里会新建子容器, Servlet WebApplicationContext
// 子容器以 Root WebApplicationContext 作为父容器
registerDispatcherServlet(servletContext);
}
// 注册DispatcherServlet
protected void registerDispatcherServlet(ServletContext servletContext) {
String servletName = getServletName();
Assert.hasLength(servletName, "getServletName() must not return null or empty");
// 创建一个Servlet WebApplicationContext
WebApplicationContext servletAppContext = createServletApplicationContext();
Assert.notNull(servletAppContext, "createServletApplicationContext() must not return null");
// 创建 DispatcherServlet
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);
// 获取Servlet拦截地址
registration.addMapping(getServletMappings());
registration.setAsyncSupported(isAsyncSupported());
// 获取过滤器
Filter[] filters = getServletFilters();
if (!ObjectUtils.isEmpty(filters)) {
for (Filter filter : filters) {
// 往ServletContext中注册过滤器
registerServletFilter(servletContext, filter);
}
}
customizeRegistration(registration);
}
// 获取Servlet名字
protected String getServletName() {
return DEFAULT_SERVLET_NAME;
}
// 创建一个子容器Servlet WebApplicationContext, 由子类实现
protected abstract WebApplicationContext createServletApplicationContext();
// 创建 DispatcherServlet
protected FrameworkServlet createDispatcherServlet(WebApplicationContext servletAppContext) {
// 这里新建一个DispatcherServlet对象, 并且将 Servlet WebApplicationContext 传递进去
return new DispatcherServlet(servletAppContext);
}
// 指定 DispatcherServlet 拦截规则, 由子类实现
protected abstract String[] getServletMappings();
// 省略代码......
}
AbstractContextLoaderInitializer
// AbstractContextLoaderInitializer.java
public abstract class AbstractContextLoaderInitializer implements WebApplicationInitializer {
/** Logger available to subclasses. */
protected final Log logger = LogFactory.getLog(getClass());
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
registerContextLoaderListener(servletContext);
}
// 往ServletContext中注册一个ContextLoaderListener
protected void registerContextLoaderListener(ServletContext servletContext) {
// 创建一个Root ApplicationContext
WebApplicationContext rootAppContext = createRootApplicationContext();
if (rootAppContext != null) {
// ContextLoaderListener实现了ServletContextListener接口
// 在 Servlet 容器启动时,例如 Tomcat、Jetty 启动
// 则会被 ContextLoaderListener 监听到
// 从而调用contextInitialized(ServletContextEvent event)方法
// 初始化 Root WebApplicationContext 容器。
// 这里新建 ContextLoaderListener 对象并且将Root ApplicationContext传递过去
ContextLoaderListener listener = new ContextLoaderListener(rootAppContext);
listener.setContextInitializers(getRootApplicationContextInitializers());
// 给ServletContext注册一个监听器
servletContext.addListener(listener);
}
else {
logger.debug("No ContextLoaderListener registered, as " +
"createRootApplicationContext() did not return an application context");
}
}
// 创建一个父容器, Root ApplicationContext, 由子类实现
@Nullable
protected abstract WebApplicationContext createRootApplicationContext();
// 省略了部分代码......
}
前面提到了 org.springframework.web.context.ContextLoaderListener
与 org.springframework.web.servlet.DispatcherServlet
对象 。
ContextLoaderListener 对象。这是一个 javax.servlet.ServletContextListener
对象,会初始化一个Root Spring WebApplicationContext 容器。DispatcherServlet对象。这是一个 javax.servlet.http.HttpServlet
对象,它除了拦截我们制定的请求外,也会初始化一个属于它的 Spring WebApplicationContext 容器,并且它是以Root Spring WebApplicationContext 作为父容器的。
Root WebApplicationContext
我们已经看到,Root WebApplicationContext 容器的初始化,通过 ContextLoaderListener 来实现。在 Servlet 容器启动时,例如 Tomcat、Jetty 启动,则会被 ContextLoaderListener 监听到,从而调用 #contextInitialized(ServletContextEvent event)
方法,初始化 Root WebApplicationContext 容器。
ContextLoaderListener实现了ServletContextListener接口并且继承了ContextLoader类。
由前面的分析可以知道,创建ContextLoaderListener对象时传递了一个WebApplicationContext对象进去。
// ContextLoaderListener.java
public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
// 无参构造方法
public ContextLoaderListener() {
}
// 有参构造方法
public ContextLoaderListener(WebApplicationContext context) {
super(context);
}
@Override
public void contextInitialized(ServletContextEvent event) {
// 调用父类 ContextLoader 的
// #initWebApplicationContext(ServletContext servletContext) 方法,
// 初始化 WebApplicationContext 对象
initWebApplicationContext(event.getServletContext());
}
// 销毁 WebApplicationContext 容器的逻辑。
@Override
public void contextDestroyed(ServletContextEvent event) {
closeWebApplicationContext(event.getServletContext());
ContextCleanupListener.cleanupAttributes(event.getServletContext());
}
}
接着看ContextLoader相关实现
public class ContextLoader {
// 无参构造函数
public ContextLoader() {
}
// 有参
public ContextLoader(WebApplicationContext context) {
this.context = context;
}
// 初始化 WebApplicationContext 对象
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
// 若已经存在 ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE 对应的 WebApplicationContext 对象,则抛出 IllegalStateException 异常。
if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
throw new IllegalStateException(
"Cannot initialize context because there is already a root application context present - " +
"check whether you have multiple ContextLoader* definitions in your web.xml!");
}
// 记录开始时间
long startTime = System.currentTimeMillis();
try {
if (this.context == null) {
this.context = createWebApplicationContext(servletContext);
}
if (this.context instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
if (!cwac.isActive()) {
if (cwac.getParent() == null) {
ApplicationContext parent = loadParentContext(servletContext);
cwac.setParent(parent);
}
// 配置 ConfigurableWebApplicationContext 对象,并刷新容器
configureAndRefreshWebApplicationContext(cwac, servletContext);
}
}
// 保存到 servletContext 中
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
// 保存到 currentContext 或 currentContextPerThread 中
ClassLoader ccl = Thread.currentThread().getContextClassLoader();
if (ccl == ContextLoader.class.getClassLoader()) {
currentContext = this.context;
}
else if (ccl != null) {
currentContextPerThread.put(ccl, this.context);
}
if (logger.isInfoEnabled()) {
long elapsedTime = System.currentTimeMillis() - startTime;
logger.info("Root WebApplicationContext initialized in " + elapsedTime + " ms");
}
return this.context;
}
catch (RuntimeException | Error ex) {
logger.error("Context initialization failed", ex);
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
throw ex;
}
}
// 配置 ConfigurableWebApplicationContext 对象,并刷新容器
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
// The application context id is still set to its original default value
// -> assign a more useful id based on available information
String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);
if (idParam != null) {
wac.setId(idParam);
}
else {
// Generate default id...
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
ObjectUtils.getDisplayString(sc.getContextPath()));
}
}
wac.setServletContext(sc);
String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
if (configLocationParam != null) {
wac.setConfigLocation(configLocationParam);
}
ConfigurableEnvironment env = wac.getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment) env).initPropertySources(sc, null);
}
// 执行一些自定义的初始化
customizeContext(sc, wac);
// 刷新 context ,执行初始化
wac.refresh();
}
}
Servlet WebApplicationContext
Servlet WebApplicationContext 容器的初始化,是在 DispatcherServlet 初始化的过程中执行。 由前面的分析可以知道,创建DispatcherServlet 对象时传递了一个WebApplicationContext对象进去。
DispatcherServlet继承了FrameworkServlet,FrameworkServlet继承了HttpServletBean,HttpServletBean继承了HttpServlet。
HttpServletBean实现了HttpServlet的init()方法
// HttpServletBean.java
public final void init() throws ServletException {
// Set bean properties from init parameters.
// 将init parameters封装到 PropertyValues pvs 中
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
if (!pvs.isEmpty()) {
try {
// 将当前的这个 Servlet 对象,转化成一个 BeanWrapper 对象。
// 从而能够以 Spring 的方式来将 pvs 注入到该 BeanWrapper 对象。
// BeanWrapper 是 Spring 提供的一个用来操作 Java Bean 属性的工具,使用它可以直接修改一个对象的属性。
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
// 注册自定义属性编辑器,一旦碰到 Resource 类型的属性,
// 将会使用 ResourceEditor 进行解析
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
// 空实现,留给子类覆盖
initBeanWrapper(bw);
// 将 pvs 注入到该 BeanWrapper 对象中
bw.setPropertyValues(pvs, true);
}
catch (BeansException ex) {
if (logger.isErrorEnabled()) {
logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
}
throw ex;
}
}
// Let subclasses do whatever initialization they like.
// 子类来实现,实现自定义的初始化逻辑
// FrameworkServlet实现了这个方法
initServletBean();
}
FrameworkServlet#initServletBean()
// FrameworkServlet.java
protected final void initServletBean() throws ServletException {
// 打日志
getServletContext().log("Initializing Spring " + getClass().getSimpleName() + " '" + getServletName() + "'");
if (logger.isInfoEnabled()) {
logger.info("Initializing Servlet '" + getServletName() + "'");
}
// 记录开始时间
long startTime = System.currentTimeMillis();
try {
// 初始化 Servlet WebApplicationContext 对象
this.webApplicationContext = initWebApplicationContext();
// 空实现。子类有需要,可以实现该方法,实现自定义逻辑
initFrameworkServlet();
}
catch (ServletException | RuntimeException ex) {
logger.error("Context initialization failed", ex);
throw ex;
}
// 打日志
if (logger.isDebugEnabled()) {
String value = this.enableLoggingRequestDetails ?
"shown which may lead to unsafe logging of potentially sensitive data" :
"masked to prevent unsafe logging of potentially sensitive data";
logger.debug("enableLoggingRequestDetails='" + this.enableLoggingRequestDetails +
"': request parameters and headers will be " + value);
}
// 打日志
if (logger.isInfoEnabled()) {
logger.info("Completed initialization in " + (System.currentTimeMillis() - startTime) + " ms");
}
}
FrameworkServlet#initWebApplicationContext()
// FrameworkServlet.java
protected WebApplicationContext initWebApplicationContext() {
// 获得Root WebApplicationContext 对象
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
// 第一种情况, 新建对象时就已经传入 webApplicationContext 属性,则直接使用
// 从前面的分析可以知道, 创建DispatcherServlet对象时传递了一个WebApplicationContext对象进来
if (this.webApplicationContext != null) {
wac = this.webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
// 未激活
if (!cwac.isActive()) {
if (cwac.getParent() == null) {
设置wac的父容器为rootContext对象
cwac.setParent(rootContext);
}
// 配置和初始化 wac
configureAndRefreshWebApplicationContext(cwac);
}
}
}
if (wac == null) {
wac = findWebApplicationContext();
}
if (wac == null) {
// No context instance is defined for this servlet -> create a local one
wac = createWebApplicationContext(rootContext);
}
if (!this.refreshEventReceived) {
// 如果未触发刷新事件,则主动触发刷新事件
synchronized (this.onRefreshMonitor) {
// FrameworkServlet 中这是个空方法
// 但是它的子类 DispatcherServlet实现了这个方法, 触发Spring MVC组件的初始化
onRefresh(wac);
}
}
if (this.publishContext) {
// 将 wac 设置到 ServletContext 中
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
}
return wac;
}
FrameworkServlet#configureAndRefreshWebApplicationContext()
// FrameworkServlet.java
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
// 如果 wac 使用了默认编号,则重新设置 id 属性
if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
// 情况一,使用 contextId 属性
if (this.contextId != null) {
wac.setId(this.contextId);
} else {
// 情况二,自动生成
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
ObjectUtils.getDisplayString(getServletContext().getContextPath()) + '/' + getServletName());
}
}
// 设置 wac 的 servletContext、servletConfig、namespace 属性
wac.setServletContext(getServletContext());
wac.setServletConfig(getServletConfig());
wac.setNamespace(getNamespace());
// 添加监听器 SourceFilteringListener 到 wac 中
wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));
// The wac environment's #initPropertySources will be called in any case when the context
// is refreshed; do it eagerly here to ensure servlet property sources are in place for
// use in any post-processing or initialization that occurs below prior to #refresh
ConfigurableEnvironment env = wac.getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
}
// 执行处理完 WebApplicationContext 后的逻辑。目前是个空方法,暂无任何实现
postProcessWebApplicationContext(wac);
// 执行ApplicationContextInitializer的initialize方法
applyInitializers(wac);
// 刷新context, 执行初始化
wac.refresh();
}