springboot简化spring的大量xml的配置文件,为了实现统一化配置管理,一般参数配置 .properties文件或者 .yml文件,统一采用main方法来进行启动,内部集成了tomcat,实现安全监控,并且支持热部署。
`
@SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } } 复制代码
调run方法之后,我们其实发现先去创建SpringApplication的实例。
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) { return run(new Class[]{primarySource}, args); } public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) { return (new SpringApplication(primarySources)).run(args); } 复制代码
`
接下来我们来继续看构建SpringApplication实例的时候,做了什么具体细节。
`
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); //推断应用类型,后面会根据类型初始化对应的环境。常用的一般都是servlet环境 this.webApplicationType = WebApplicationType.deduceFromClasspath(); ////初始化classpath下 META-INF/spring.factories中已配置的ApplicationContextInitializer setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); //初始化classpath下所有已配置的 ApplicationListener setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); //根据调用栈,推断出main方法的类名 this.mainApplicationClass = deduceMainApplicationClass(); } 复制代码
`
获取ApplicationContextInitializer实例的对象,会调用initialize方法。
`
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) { ClassLoader classLoader = getClassLoader(); // Use names and ensure unique to protect against duplicates Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader)); //创建springFactories实例 List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); AnnotationAwareOrderComparator.sort(instances); return instances; } 复制代码
`
获取ApplicationListener实例的对象,其中onApplicationEvent可以监听全部事件。
`
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) { ClassLoader classLoader = getClassLoader(); // Use names and ensure unique to protect against duplicates Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader)); List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); AnnotationAwareOrderComparator.sort(instances); return instances; } 复制代码
`
`
public ConfigurableApplicationContext run(String... args) { //记录程序运行时间 StopWatch stopWatch = new StopWatch(); stopWatch.start(); //ConfigurableApplicationContext Spring上下文 ConfigurableApplicationContext context = null; Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>(); configureHeadlessProperty(); //从META-INF/spring.factories中获取监听器 //1、获取并启动监听器。 SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting(); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); //2、构建应用上下文环境 ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); configureIgnoreBeanInfo(environment); //打印banner Banner printedBanner = printBanner(environment); //3、初始化应用上下文 context = createApplicationContext(); //实例化SpringBootExceptionReporter.class,用来支持报告 //关于启动报错 exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context); //4、刷新应用上下文前的准备阶段 prepareContext(context, environment, listeners, applicationArguments, printedBanner); //5、刷新应用上下文 refreshContext(context); //刷新应用上下文后的扩展接口 afterRefresh(context, applicationArguments); //时间记录停止 stopWatch.stop(); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch); } //发布容器启动完成事件 listeners.started(context); callRunners(context, applicationArguments); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, listeners); throw new IllegalStateException(ex); } try { listeners.running(context); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, null); throw new IllegalStateException(ex); } return context; } 复制代码
`
我们这里来说明一下SpringApplication的run方法做了什么事情。
第一步:获取并启动监听器
第二步:构造应用上下文环境
第三步:初始化应用上下文
第四步:刷新应用上下文前的准备阶段
第五步:刷新应用上下文
第六步:刷新应用上下文后的扩展接口
`
private SpringApplicationRunListeners getRunListeners(String[] args) { Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class }; return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args)); } private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) { ClassLoader classLoader = getClassLoader(); // Use names and ensure unique to protect against duplicates Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader)); List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); AnnotationAwareOrderComparator.sort(instances); return instances; } 复制代码
`
小伙伴有没有发现跟上面的ApplicationListener加载业务逻辑一样。EventPublishingRunListener开始监听事件。
构建应用上下文环境是去加载springboot的*.properties或者*.yml文件。 `
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) { // Create and configure the environment //创建并且配置相应的环境 ConfigurableEnvironment environment = getOrCreateEnvironment(); //根据用户配置,配置environment系统环境 configureEnvironment(environment, applicationArguments.getSourceArgs()); //启动相应的监听器,其中一个重要的监听器 //ConfigFileApplicationListener 就是加载项目配置文件的监听器。 listeners.environmentPrepared(environment); //绑定springbootApplication环境 bindToSpringApplication(environment); if (!this.isCustomEnvironment) { environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment, deduceEnvironmentClass()); } ConfigurationPropertySources.attach(environment); return environment; } 复制代码
`
springboot应用上下文的环境,我们以SERVLET环境为例。
`
/** * The application should not run as a web application and should not start an * embedded web server. */ NONE, /** * The application should run as a servlet-based web application and should start an * embedded servlet web server. */ SERVLET, /** * The application should run as a reactive web application and should start an * embedded reactive web server. */ REACTIVE; 复制代码
`
下面是获取context上下文实例。 `
/** * The class name of application context that will be used by default for non-web * environments. */ public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context." + "annotation.AnnotationConfigApplicationContext"; /** * The class name of application context that will be used by default for web * environments. */ public static final String DEFAULT_SERVLET_WEB_CONTEXT_CLASS = "org.springframework.boot." + "web.servlet.context.AnnotationConfigServletWebServerApplicationContext"; /** * The class name of application context that will be used by default for reactive web * environments. */ public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework." + "boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext"; //获取context实例 protected ConfigurableApplicationContext createApplicationContext() { Class<?> contextClass = this.applicationContextClass; if (contextClass == null) { try { switch (this.webApplicationType) { case SERVLET: contextClass = Class.forName(DEFAULT_SERVLET_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); } 复制代码
` 对应三种类型,web应用构建上下文默认为AnnotationConfigServletWebServerApplicationContext。我们可以来看一下AnnotationConfigServletWebServerApplicationContext的实例图。
此图的继承关系如下:
AnnotationConfigServletWebServerApplicationContext类继承了ServletWebServerApplicationContext
ServletWebServerApplicationContext继承了GenericWebApplicationContext
GenericWebApplicationContext继承了GenericApplicationContext
GenericApplicationContext继承了AbstractApplicationContext
AbstractApplicationContext继承了DefaultResourceLoader
应用上下文可以理解成容器的高级表现形式,应用上下文确实在IOC容器基础上丰富了一些高级功能。