前两篇 (Spring MVC 源码 --Root webApplicationContext 和 Spring MVC 源码 --Servlet WebApplicationContext) 讲述了 springmvc 项目创建上下文的过程, 这一篇带大家了解一下 springboot 项目创建上下文的过程.
SpringApplication 引导类
SpringApplication 类用于启动或者引导 springboot 项目, 直接应用在 java main 方法中.
- public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
- this.resourceLoader = resourceLoader;
- Assert.notNull(primarySources, "PrimarySources must not be null");
- this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
- // 判断当前 Web 应用程序类型
- this.webApplicationType = deduceWebApplicationType();
- // 找到 * META-INF/spring.factories * 中声明的所有 ApplicationContextInitializer 的实现类并将其实例化
- setInitializers((Collection) getSpringFactoriesInstances(
- ApplicationContextInitializer.class));
- // 找到 * META-INF/spring.factories * 中声明的所有 ApplicationListener 的实现类并将其实例化
- setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
- // 获得当前执行 main 方法的类对象
- this.mainApplicationClass = deduceMainApplicationClass();
- }
springboot 项目 WebApplicationType 分为三种: 非 Web 类型, Web 类型(spring-mvc), 响应式 Web 类型(spring-webflux)
- private static final String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet",
- "org.springframework.web.context.ConfigurableWebApplicationContext" };
- private static final String REACTIVE_WEB_ENVIRONMENT_CLASS = "org.springframework."
- + "web.reactive.DispatcherHandler";
- private static final String MVC_WEB_ENVIRONMENT_CLASS = "org.springframework."
- + "web.servlet.DispatcherServlet";
- private WebApplicationType deduceWebApplicationType() {
- if (ClassUtils.isPresent(REACTIVE_WEB_ENVIRONMENT_CLASS, null)
- && !ClassUtils.isPresent(MVC_WEB_ENVIRONMENT_CLASS, null)) {
- return WebApplicationType.REACTIVE;
- }
- for (String className : WEB_ENVIRONMENT_CLASSES) {
- if (!ClassUtils.isPresent(className, null)) {
- return WebApplicationType.NONE;
- }
- }
- return WebApplicationType.SERVLET;
- }
下面的 run 方法是 springboot 项目启动的核心代码.
- public ConfigurableApplicationContext run(String... args) {
- // 开启任务执行时间监听器
- StopWatch stopWatch = new StopWatch();
- stopWatch.start();
- ConfigurableApplicationContext context = null;
- Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
- // 设置系统属性『java.awt.headless』, 为 true 则启用 headless 模式支持
- configureHeadlessProperty();
- // 通过 * SpringFactoriesLoader * 检索 * META-INF/spring.factories*,
- // 找到声明的所有 SpringApplicationRunListener 的实现类并将其实例化,
- // 之后逐个调用其 started()方法, 广播 SpringBoot 要开始执行了.
- SpringApplicationRunListeners listeners = getRunListeners(args);
- listeners.starting();
- try {
- ApplicationArguments applicationArguments = new DefaultApplicationArguments(
- args);
- // 创建并配置当前 SpringBoot 应用将要使用的 Environment(包括配置要使用的 PropertySource 以及 Profile),
- // 并遍历调用所有的 SpringApplicationRunListener 的 environmentPrepared()方法, 广播 Environment 准备完毕.
- ConfigurableEnvironment environment = prepareEnvironment(listeners,
- applicationArguments);
- // 是否搜索 BeanInfo 类
- configureIgnoreBeanInfo(environment);
- //Banner 打印
- Banner printedBanner = printBanner(environment);
- // 根据 WebApplicationType 的值来决定创建何种类型的 ApplicationContext 对象
- context = createApplicationContext();
- // 通过 * SpringFactoriesLoader * 检索 * META-INF/spring.factories*, 获取并实例化异常分析器
- exceptionReporters = getSpringFactoriesInstances(
- SpringBootExceptionReporter.class,
- new Class[] { ConfigurableApplicationContext.class }, context);
- // 为 ApplicationContext 加载 environment, 之后逐个执行 ApplicationContextInitializer 的 initialize()方法来进一步封装 ApplicationContext,
- // 并调用所有的 SpringApplicationRunListener 的 contextPrepared()方法,[EventPublishingRunListener 只提供了一个空的 contextPrepared()方法] ,
- // 之后初始化 IoC 容器, 并调用 SpringApplicationRunListener 的 contextLoaded()方法, 广播 ApplicationContext 的 IoC 加载完成,
- // 这里就包括通过 **@EnableAutoConfiguration** 导入的各种自动配置类.
- prepareContext(context, environment, listeners, applicationArguments,
- printedBanner);
- // 初始化所有自动配置类, 调用 ApplicationContext 的 refresh()方法
- refreshContext(context);
- // 空方法
- afterRefresh(context, applicationArguments);
/ 关闭任务执行时间监听器
- stopWatch.stop();
- // 如果开启日志, 则打印执行是时间
- if (this.logStartupInfo) {
- new StartupInfoLogger(this.mainApplicationClass)
- .logStarted(getApplicationLog(), stopWatch);
- }
- // 调用所有的 SpringApplicationRunListener 的 started()方法, 广播 SpringBoot 已经完成了 ApplicationContext 初始化的全部过程.
- listeners.started(context);
- // 遍历所有注册的 ApplicationRunner 和 CommandLineRunner, 并执行其 run()方法.
- // 我们可以实现自己的 ApplicationRunner 或者 CommandLineRunner, 来对 SpringBoot 的启动过程进行扩展.
- callRunners(context, applicationArguments);
- }
- catch (Throwable ex) {
- handleRunFailure(context, ex, exceptionReporters, listeners);
- throw new IllegalStateException(ex);
- }
- try {
- // 调用所有的 SpringApplicationRunListener 的 running()方法, 广播 SpringBoot 已经可以处理服务请求了.
- listeners.running(context);
- }
- catch (Throwable ex) {
- handleRunFailure(context, ex, exceptionReporters, null);
- throw new IllegalStateException(ex);
- }
- return context;
- }
由上文可知, 默认 WebApplicationType 是 WebApplicationType.SERVLET, 所以默认的上下文是 AnnotationConfigServletWebServerApplicationContext.
- // 应用程序非 Web 环境
- public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context."
- + "annotation.AnnotationConfigApplicationContext";
- // 应用程序 Web 环境
- public static final String DEFAULT_WEB_CONTEXT_CLASS = "org.springframework.boot."
- + "web.servlet.context.AnnotationConfigServletWebServerApplicationContext";
- // 应用程序响应式 Web 环境
- public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework."
- + "boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext";
- public enum WebApplicationType {
- // 应用程序不需要任何应用服务器
- NONE,
- // 应用程序内嵌 Web 服务器
- SERVLET,
- // 应用程序内嵌响应式 Web 服务器
- REACTIVE
- }
- protected ConfigurableApplicationContext createApplicationContext() {
- Class<?> contextClass = this.applicationContextClass;
- if (contextClass == null) {
- try {
- switch (this.webApplicationType) {
- case SERVLET:
- contextClass = Class.forName(DEFAULT_WEB_CONTEXT_CLASS);
- break;
- case REACTIVE:
- contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
- break;
- default:
- contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
- }
- }
- catch (ClassNotFoundException ex) {
- throw new IllegalStateException(
- "Unable create a default ApplicationContext,"
- + "please specify an ApplicationContextClass",
- ex);
- }
- }
- return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
- }
AnnotationConfigServletWebServerApplicationContext 类结构层次如下.
父类 ServletWebServerApplicationContext 创建内嵌 Web 应用服务器如下.
- @Override
- protected void onRefresh() {
- super.onRefresh();
- try {
- // 创建 Web 应用服务
- createWebServer();
- }
- catch (Throwable ex) {
- throw new ApplicationContextException("Unable to start web server", ex);
- }
- }
- private void createWebServer() {
- WebServer webServer = this.webServer;
- ServletContext servletContext = getServletContext();
- if (webServer == null && servletContext == null) {
- // 获取 ServletWebServerFactory 类型的 Web 服务器工厂类, 比如 TomcatServletWebServerFactory,JettyServletWebServerFactory,UndertowServletWebServerFactory
- ServletWebServerFactory factory = getWebServerFactory();
- this.webServer = factory.getWebServer(getSelfInitializer());
- }
- else if (servletContext != null) {
- try {
- getSelfInitializer().onStartup(servletContext);
- }
- catch (ServletException ex) {
- throw new ApplicationContextException("Cannot initialize servlet context",
- ex);
- }
- }
- initPropertySources();
- }
Springmvc 项目上下文和 Springboot 项目上下文浅析
Springmvc 项目上下文
Springmvc 项目的 rootcontext 的创建时通过 xml 中 配置的 org.springframework.Web.context.ContextLoaderListener, 其父类 ContextLoader 中有一个初始化上下文的方法, 如下.
public WebApplicationContext initWebApplicationContext(ServletContext servletContext);
上下文创建好之后调用
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
其中 servletContext 的实例是 org.apache.catalina.core.ApplicationContext;
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = org.springframework.Web.context.WebApplicationContext.ROOT)
这样 rootcontext 就创建好了, 并且放入了 servletContext 中保存. rootcontext 获取方式如下.
WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
Springmvc 项目中的 DispatcherServlet 是在 xml 中按照 servlet 格式配置的, 这种方式创建的 servlet 实例没有被 spring 容器管理.
DispatcherServlet 实现了 ApplicationContextAware 接口, 有一个成员变量来保存此 servlet 对应的上下文, 如下.
- /** WebApplicationContext for this servlet */
- private WebApplicationContext webApplicationContext;
这种情况下 webApplicationContext 变量是无法注入的[DispatcherServlet 实例没有被 spring 容器管理] . 看一下 DispatcherServlet 的父类 FrameworkServlet 是如何初始化上下文的, 如下.
- protected WebApplicationContext initWebApplicationContext() {
- WebApplicationContext rootContext =
- WebApplicationContextUtils.getWebApplicationContext(getServletContext());
- WebApplicationContext wac = null;
- //springmvc 项目这块的判断为 false;springboot 项目为 ture.
- if (this.webApplicationContext != null) {
- ......
DispatcherServlet 所属上下文的存储
- protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
- ...
- request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
- ...
- }
DispatcherServlet 所属上下文获取 org.springframework.Web.servlet.support.RequestContextUtils.
Springboot 项目上下文
Springboot 项目中, 调试代码时发现 DispatcherServlet 的父类 FrameworkServlet 在初始化上下文的时候 rootcontext 和 DispatcherServlet 成员变量 webApplicationContext 保存的是一个实例, 即 AnnotationConfigServletWebServerApplicationContext 实例.
上面也提到了 DispatcherServlet[对应的实例被 spring 容器管理] 实现了 ApplicationContextAware 接口, webApplicationContext 保存的上下文是通过自动注入而来.
RootContext(AnnotationConfigServletWebServerApplicationContext)保存到 servletcontext 中的操作, 如下.
- //ServletWebServerApplicationContext
- protected void prepareWebApplicationContext(ServletContext servletContext) {
- ...
- servletContext.setAttribute(
- WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this);
- ...
- }
总结
来源: https://www.cnblogs.com/hujunzheng/p/10854464.html