spring启动流程 (6完结) springmvc启动流程

SpringMVC的启动入口在SpringServletContainerInitializer类,它是ServletContainerInitializer实现类(Servlet3.0新特性)。在实现方法中使用WebApplicationInitializer创建ApplicationContext、创建注册DispatcherServlet、初始化ApplicationContext等。

SpringMVC已经将大部分的启动逻辑封装在了几个抽象WebApplicationInitializer中,开发者只要继承这些抽象类实现抽象方法即可。

本文将详细分析ServletContainerInitializer、SpringServletContainerInitializer和WebApplicationInitializer的工作流程。

Servlet3.0的ServletContainerInitializer

ServletContainerInitializer接口

ServletContainerInitializer是Servlet3.0的接口。

该接口在web应用程序启动阶段接收通知,注册servlet、filter、listener等。

该接口的实现类可以用HandlesTypes进行标注,并指定一个Class值,后续会将实现、扩展了这个Class的类集合作为参数传递给onStartup方法。

以tomcat为例,在容器配置初始化阶段,将使用SPI查找实现类,在ServletContext启动阶段,初始化并调用onStartup方法来进行ServletContext的初始化。

SpringServletContainerInitializer实现类

在onStartup方法创建所有的WebApplicationInitializer对并调用onStartup方法。

以下为SPI配置,在spring-web/src/main/resources/META-INF/services/javax.servlet.ServletContainerInitializer文件:

WebApplicationInitializer接口

WebApplicationInitializer接口概述

Interface to be implemented in Servlet 3.0+ environments in order to configure the ServletContext programmatically -- as opposed to (or possibly in conjunction with) the traditional web.xml-based approach.

Implementations of this SPI will be detected automatically by SpringServletContainerInitializer, which itself is bootstrapped automatically by any Servlet 3.0 container. See its Javadoc for details on this bootstrapping mechanism.

示例:

java 复制代码
public class MyWebAppInitializer implements WebApplicationInitializer {

   @Override
   public void onStartup(ServletContext container) {
     // Create the 'root' Spring application context
     AnnotationConfigWebApplicationContext rootContext =
       new AnnotationConfigWebApplicationContext();
     rootContext.register(AppConfig.class);

     // Manage the lifecycle of the root application context
     container.addListener(new ContextLoaderListener(rootContext));

     // Create the dispatcher servlet's Spring application context
     AnnotationConfigWebApplicationContext dispatcherContext =
       new AnnotationConfigWebApplicationContext();
     dispatcherContext.register(DispatcherConfig.class);

     // Register and map the dispatcher servlet
     ServletRegistration.Dynamic dispatcher =
       container.addServlet("dispatcher", new DispatcherServlet(dispatcherContext));
     dispatcher.setLoadOnStartup(1);
     dispatcher.addMapping("/");
   }
}

开发者可以编写类继承AbstractAnnotationConfigDispatcherServletInitializer抽象类,这个抽象类已经将大部分的初始化逻辑进行了封装。

WebApplicationInitializer实现和继承关系

AbstractContextLoaderInitializer类:

Convenient base class for WebApplicationInitializer implementations that register a ContextLoaderListener in the servlet context. The only method required to be implemented by subclasses is createRootApplicationContext(), which gets invoked from registerContextLoaderListener(ServletContext).

AbstractDispatcherServletInitializer类:

Base class for WebApplicationInitializer implementations that register a DispatcherServlet in the servlet context. Most applications should consider extending the Spring Java config subclass AbstractAnnotationConfigDispatcherServletInitializer.

AbstractAnnotationConfigDispatcherServletInitializer类:

WebApplicationInitializer to register a DispatcherServlet and use Java-based Spring configuration.

Implementations are required to implement:

  • getRootConfigClasses() -- for "root" application context (non-web infrastructure) configuration.
  • getServletConfigClasses() -- for DispatcherServlet application context (Spring MVC infrastructure) configuration.

If an application context hierarchy is not required, applications may return all configuration via getRootConfigClasses() and return null from getServletConfigClasses().

开发者SpringMvcInitializer示例

开发者需要编写类继承AbstractAnnotationConfigDispatcherServletInitializer类,实现几个抽象方法:

java 复制代码
public class SpringMvcInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

  /**
   * 指定创建root application context需要的@Configuration/@Component类
   */
  @Override
  protected Class<?>[] getRootConfigClasses() {
    return new Class[]{AppConfig.class};
  }

  /**
   * 指定创建Servlet application context需要的@Configuration/@Component类
   * 如果所有的配置类都使用root config classes就返回null
   */
  @Override
  protected Class<?>[] getServletConfigClasses() {
    return null;
  }

  /**
   * Specify the servlet mapping(s) for the DispatcherServlet --- for example "/", "/app", etc.
   */
  @Override
  protected String[] getServletMappings() {
    return new String[]{"/"};
  }
}

SpringMVC启动流程

入口AbstractDispatcherServletInitializer.onStartup方法

java 复制代码
public void onStartup(ServletContext servletContext) throws ServletException {
	super.onStartup(servletContext);
	registerDispatcherServlet(servletContext);
}

父类的onStartup方法创建RootApplicationContext、注册ContextLoaderListener监听器:

java 复制代码
public void onStartup(ServletContext servletContext) throws ServletException {
	registerContextLoaderListener(servletContext);
}

protected void registerContextLoaderListener(ServletContext servletContext) {
	WebApplicationContext rootAppContext = createRootApplicationContext();
	if (rootAppContext != null) {
		// ContextLoaderListener是ServletContextListener
		// 会在contextInitialized阶段初始化RootApplicationContext
		ContextLoaderListener listener = new ContextLoaderListener(rootAppContext);
		listener.setContextInitializers(getRootApplicationContextInitializers());
		servletContext.addListener(listener);
	}
}

注册DispatcherServlet

registerDispatcherServlet方法用于创建ServletApplicationContext、注册DispatcherServlet:

java 复制代码
protected void registerDispatcherServlet(ServletContext servletContext) {
	String servletName = getServletName();

	// 创建ServletApplicationContext
	WebApplicationContext servletAppContext = createServletApplicationContext();

	// 创建DispatcherServlet
	FrameworkServlet dispatcherServlet = createDispatcherServlet(servletAppContext);
	// 添加ApplicationContextInitializer集,会在初始化时调用
	dispatcherServlet.setContextInitializers(getServletApplicationContextInitializers());

	// 添加到ServletContext
	ServletRegistration.Dynamic registration = servletContext.addServlet(servletName, dispatcherServlet);

	registration.setLoadOnStartup(1);
	registration.addMapping(getServletMappings());
	registration.setAsyncSupported(isAsyncSupported());

	Filter[] filters = getServletFilters();
	if (!ObjectUtils.isEmpty(filters)) {
		for (Filter filter : filters) {
			registerServletFilter(servletContext, filter);
		}
	}

	customizeRegistration(registration);
}

触发ContextLoaderListener监听器contextInitialized事件

这个是Servlet的ServletContextListener机制,在ServletContext创建之后触发contextInitialized事件:

java 复制代码
public void contextInitialized(ServletContextEvent event) {
	initWebApplicationContext(event.getServletContext());
}

public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
	// 判断是否已经在当前ServletContext绑定了WebApplicationContext
	// 如果已经绑定抛错
	if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
		throw new IllegalStateException("已经初始化过了");
	}

	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);
				}
				// refresh
				configureAndRefreshWebApplicationContext(cwac, servletContext);
			}
		}
		servletContext
            .setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);

		ClassLoader ccl = Thread.currentThread().getContextClassLoader();
		if (ccl == ContextLoader.class.getClassLoader()) {
			currentContext = this.context;
		} else if (ccl != null) {
			currentContextPerThread.put(ccl, this.context);
		}

		return this.context;
	} catch (RuntimeException | Error ex) {
		servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
		throw ex;
	}
}

protected void configureAndRefreshWebApplicationContext(
		ConfigurableWebApplicationContext wac, ServletContext sc) {
	// 给wac设置id

	wac.setServletContext(sc);
	// 设置spring主配置文件路径
	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);
	// 刷新ApplicationContext
	wac.refresh();
}

触发DispatcherServlet的init事件

Servlet在接收请求之前会调用其init方法进行初始化,这个是Servlet的规范。

init方法在其父类HttpServletBean中:

java 复制代码
public final void init() throws ServletException {

	// 从ServletConfig获取配置设置到Servlet
	PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
	if (!pvs.isEmpty()) {
		try {
			BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
			ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
			bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
			initBeanWrapper(bw);
			bw.setPropertyValues(pvs, true);
		} catch (BeansException ex) {
			throw ex;
		}
	}

	// 初始化
	initServletBean();
}

// FrameworkServlet
protected final void initServletBean() throws ServletException {
	try {
		// 初始化ServletApplicationContext
		this.webApplicationContext = initWebApplicationContext();
		initFrameworkServlet();
	} catch (ServletException | RuntimeException ex) {
		throw ex;
	}
}

protected WebApplicationContext initWebApplicationContext() {
	// 获取rootApplicationContext
	// 之前的ServletContext初始化阶段已经绑定
	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()) {
				// 将rootApplicationContext设置为Parent
				if (cwac.getParent() == null) {
					cwac.setParent(rootContext);
				}
				// 刷新ApplicationContext
				configureAndRefreshWebApplicationContext(cwac);
			}
		}
	}
	// 如果没有需要查找或创建
	if (wac == null) {
		wac = findWebApplicationContext();
	}
	if (wac == null) {
		wac = createWebApplicationContext(rootContext);
	}

	if (!this.refreshEventReceived) {
		synchronized (this.onRefreshMonitor) {
			// 子类实现
			onRefresh(wac);
		}
	}

	if (this.publishContext) {
		String attrName = getServletContextAttributeName();
		getServletContext().setAttribute(attrName, wac);
	}

	return wac;
}

// DispatcherServlet
protected void onRefresh(ApplicationContext context) {
	initStrategies(context);
}

// 初始化SpringMVC相关组件
protected void initStrategies(ApplicationContext context) {
	initMultipartResolver(context);
	initLocaleResolver(context);
	initThemeResolver(context);
	initHandlerMappings(context);
	initHandlerAdapters(context);
	initHandlerExceptionResolvers(context);
	initRequestToViewNameTranslator(context);
	initViewResolvers(context);
	initFlashMapManager(context);
}

SpringMVC启动流程总结

  • 创建RootApplicationContext、注册ContextLoaderListener监听器
  • 创建ServletApplicationContext、注册DispatcherServlet
  • 触发ContextLoaderListener监听器contextInitialized事件,初始化RootApplicationContext
  • 触发DispatcherServlet的init事件,初始化ServletApplicationContext
相关推荐
weixin_46242847几秒前
使用 Caffeine 缓存并在业务方法上通过注解实现每3到5秒更新缓存
java·缓存
程序媛小果2 分钟前
基于java+SpringBoot+Vue的桂林旅游景点导游平台设计与实现
java·vue.js·spring boot
骑鱼过海的猫1234 分钟前
【java】java通过s3访问ceph报错
java·ceph·iphone
杨充10 分钟前
13.观察者模式设计思想
java·redis·观察者模式
Lizhihao_12 分钟前
JAVA-队列
java·开发语言
喵叔哟21 分钟前
重构代码之移动字段
java·数据库·重构
喵叔哟21 分钟前
重构代码之取消临时字段
java·前端·重构
fa_lsyk24 分钟前
maven环境搭建
java·maven
Daniel 大东43 分钟前
idea 解决缓存损坏问题
java·缓存·intellij-idea
2401_8576363944 分钟前
共享汽车管理新纪元:SpringBoot框架应用
数据库·spring boot·汽车