前两篇(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项目上下文
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容器管理。
/** 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项目上下文
//ServletWebServerApplicationContext
protected void prepareWebApplicationContext(ServletContext servletContext) { ... servletContext.setAttribute( WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this); ... }
总结
经过三篇文章的分析,相信大家已经明白了Springmvc项目默认是有两个上下文(Root webapplicationcontext 和 Servlet webapplicationcontext,对应的类型是XmlServletWebServerApplicationContext),而Springboot项目默认是一个上下文,对应的类型是AnnotationConfigServletWebServerApplicationContext。如果有什么疑问,请关注订阅号,进行私聊。