1.创建SpringApplication实例
在run方法上打个断点,启动项目,开始根据项目的启动
一直step into进去,发现在run方法里面new了一个上下文对象SpringApplication。
SpringApplicaton是整个应用的管理中心,这里创建了一个初始化了一些springboot基本的配置的对象
/** * Create a new {@link SpringApplication} instance. The application context will load * beans from the specified primary sources (see {@link SpringApplication class-level} * documentation for details. The instance can be customized before calling * {@link #run(String...)}. * @param resourceLoader the resource loader to use * @param primarySources the primary bean sources * @see #run(Class, String[]) * @see #setSources(Set) */ @SuppressWarnings({ "unchecked", "rawtypes" }) public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); this.webApplicationType = WebApplicationType.deduceFromClasspath(); setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); this.mainApplicationClass = deduceMainApplicationClass(); }这里要注意看构造方法,step into进去SpringApplication构造方法里面可以看到构造方法做了一下基础数据的设置:
设置资源加载器设置主类设置应用类型设置初始化器设置监听器设置启动类(启动方法mian所在类)这里我们主要关注设置初始化器和监听器的过程。
这里两个过程调用的是同一个方法getSpringFactoriesInstances,两次调用传了不一样的参数返回了两个不一样的工厂实例集合。
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) { ClassLoader classLoader = getClassLoader(); // 这一步spring内置配置文件META-INF/spring.factories中读取一个kv形式的集合 // 其中key对应的就是调用getSpringFactoriesInstances方法时传入的参数 // 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; }这里通过调用SpringFactoriesLoader.loadFactoryNames(type, classLoader)获取到的names集合其实是从内置的资源文件中读取出来的一个类名集合
* springboot内置文件 META-INF/spring.factories
使用传入参数搜索一下,原则上getSpringFactoriesInstances(ApplicationContextInitializer.class)应该返回了5个初始化器实例
但是要注意项目中可能多个包下都存在META-INF/spring.factories文件,所以这里读取到的是全部META-INF/spring.factories文件中org.springframework.context.ApplicationContextInitializer指向的值的并集,所以这里getSpringFactoriesInstances有可能返回大于5个初始化器实例。
获取到类名集合后通过构造方法实例化对象
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);监听器设置过程原理类似
* 后面也会发现启动过程中有多处都调用了getSpringFactoriesInstances方法获取工厂实例
2.通过SpringApplication实例启动应用
创建好SpringApplication实例后调用了该对象的run方法
/** * Run the Spring application, creating and refreshing a new * {@link ApplicationContext}. * @param args the application arguments (usually passed from a Java main method) * @return a running {@link ApplicationContext} */ public ConfigurableApplicationContext run(String... args) { StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>(); configureHeadlessProperty(); SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting(); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); configureIgnoreBeanInfo(environment); Banner printedBanner = printBanner(environment); context = createApplicationContext(); exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context); prepareContext(context, environment, listeners, applicationArguments, printedBanner); refreshContext(context); afterRefresh(context, applicationArguments); stopWatch.stop(); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWa } 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; }方法注释翻译过来意思是“启动Spring应用,创建一个ApplicationContext上下文对象并刷新上下文”
方法一开始就是创建一个StopWatch对象并启动,StopWatch是一个计时器,用于记录应用的启动耗时。
StopWatch stopWatch = new StopWatch(); stopWatch.start();所以我们可以把关注点放在计时器开始到结束这个过程。
创建SpringApplicationRunListeners对象
SpringApplicationRunListeners listeners = getRunListeners(args);getRunListeners方法事实上也是调用了getSpringFactoriesInstances方法得到一个工厂实例集合,把这个集合封装到一个SpringApplicationRunListeners对象里面,SpringApplicationRunListeners对象用于监听run方法的执行过程
private SpringApplicationRunListeners getRunListeners(String[] args) { Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class }; return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args)); }启动run监听器
listeners.starting(); DefaultApplicationArguments用于解释启动应用时输入的命令行参数,例如使用一下命令启动 $ java -jar --spring.config.name=demoApplication --logging.path=/dev/logs --logging.level.root=info testArgDefaultApplicationArguments可以将--spring.config.name=demoApplication”分解为kv集合和v集合两种形式,即“spring.config.name”=“demoApplication”
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);可以看到分解结果有三对kv值和一个v值
准备运行环境
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);prepareEnvironment方法创建一个新的运行环境,并设置好环境属性、将监听器绑定到该运行环境上、绑定到当前运行的应用SpringApplication。
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) { // Create and configure the environment ConfigurableEnvironment environment = getOrCreateEnvironment();//获取或创建一个运行环境 configureEnvironment(environment, applicationArguments.getSourceArgs());//配置运行环境 ConfigurationPropertySources.attach(environment);//绑定动态解释器,跟踪配置的改动 listeners.environmentPrepared(environment);//将监听器绑定到运行环境上 bindToSpringApplication(environment);//将当前应用绑定到该运行环境上 if (!this.isCustomEnvironment) { environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment, deduceEnvironmentClass()); } ConfigurationPropertySources.attach(environment); return environment; }configureIgnoreBeanInfo配置是否忽略java.beans.BeanInfo类的扫描
private void configureIgnoreBeanInfo(ConfigurableEnvironment environment) { if (System.getProperty(CachedIntrospectionResults.IGNORE_BEANINFO_PROPERTY_NAME) == null) { Boolean ignore = environment.getProperty("spring.beaninfo.ignore", Boolean.class, Boolean.TRUE); System.setProperty(CachedIntrospectionResults.IGNORE_BEANINFO_PROPERTY_NAME, ignore.toString()); } }方法使用System.setProperty方式将属性设置到全局变量中
这一行代码是打印banner图
Banner printedBanner = printBanner(environment);例如springboot默认的banner图
. ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.3.1.RELEASE)创建一个上下文实例
context = createApplicationContext();创建异常报告器实例,用于收集处理启动过程中的异常报告
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class ,new Class[] { ConfigurableApplicationContext.class }, context);接下来的两个方法prepareContext和refreshContext是应用启动的核心过程,里面包括运行环境的准备、各种监听器的设置、bean工厂创建、包扫描、bean初始化、bean强化、依赖注释等等 。在《SpringBoot源码阅读(三)》中再详细解读。
prepareContext(context, environment, listeners, applicationArguments, printedBanner); refreshContext(context);紧接着的是afterRefresh方法,springboot没有对该方法进行任何实现,仅用于用户拓展。就是说我们可以通过实现这个方法达到在springboot内部驱动过程执行完后接着执行我们自定一个的业务逻辑的目的。
afterRefresh(context, applicationArguments);接着就是停止计时器、打印启动耗时等日志,启动ApplicationRunner和CommandLineRunner两种类型的runner
最后启动监听器,应用启动完成
listeners.running(context);