Spring Boot启动过程(一)
之前在排查一个线上问题时,不得不仔细跑了很多遍 Spring Boot 的代码,于是整理一下,我用的是 1.4.3.RELEASE。
首先,普通的入口,这没什么好说的,我就随便贴贴代码了:
SpringApplication.run(Application.class, args);
-->
public static ConfigurableApplicationContext run(Object source, String... args) {
return run(new Object[] {source}, args);
}
public static ConfigurableApplicationContext run(Object[] sources, String[] args) {
return new SpringApplication(sources).run(args);
}
也就是一个静态方法,调用了构造函数创建实例,构造的参数是 Object 数组,这里 new 这个数组的时候传入了一个元素就是启动类的类对象实例 (一般就是“new Object[] {Application.class”}),构造函数里调用了一个 initialize 方法。
SpringApplication 的 initialize 方法,首先在 Object 数组有值的情况下将数组放入一个 final 的类实例私有 Object 的 Set 集合中;然后用 deduceWebEnvironment 方法判断当前应用环境是否是 web 环境,判断逻辑是看 Classpath 是否同时存在 javax.servlet.Servlet 和 org.springframework.web.context.ConfigurableWebApplicationContext,缺一就认为不是。然后,调用 setInitializers 方法,设置类实例的私有 List<ApplicationContextInitializer<?>> 类型变量 initializers:
public void setInitializers( Collection<? extends ApplicationContextInitializer<?>> initializers) { this.initializers = new ArrayList<ApplicationContextInitializer<?>>(); this.initializers.addAll(initializers); }
设置的时候会先 new,也就是说这方法每次都是整体更换,不会追加。这个方法的参数都是各个模块中配置在 META-INF/spring.factories 中的 key 为 org.springframework.context.ApplicationContextInitializer 的值,这些类都是接口 ApplicationContextInitializer<C extends ConfigurableApplicationContext> 的泛型实现。
private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); // Use names and ensure unique to protect against duplicates Set<String> names = new LinkedHashSet<String>(SpringFactoriesLoader.loadFactoryNames(type, classLoader)); List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); AnnotationAwareOrderComparator.sort(instances); return instances; }
使用 SpringFactoriesLoader.loadFactoryNames 方法去取上面说的被配置的 ApplicationContextInitializer 的名字放进 Set<String> 中,并用反射创建这些名字的实例。
setInitializers 方法之后又是 setInitializers,参数同上都是 getSpringFactoriesInstances 方法获取,只不过这次参数 Class<T> type 泛型类型是 org.springframework.context.ApplicationListener。
initialize 方法的最后一个步是设置实例的 Class<?> 类型私有属性 mainApplicationClass,获取设置值的方法 deduceMainApplicationClass:
private Class<?> deduceMainApplicationClass() { try {StackTraceElement[] stackTrace = new RuntimeException().getStackTrace(); for (StackTraceElement stackTraceElement : stackTrace) { if ("main".equals(stackTraceElement.getMethodName())) { return Class.forName(stackTraceElement.getClassName()); } } } catch (ClassNotFoundException ex) { // Swallow and continue } return null; }
实例化 SpringApplication 后调用了它的 run 实例方法(注意不是上面的静态方法)。一进 run 方法首先启动了 StopWatch,这个 StopWatch 的功能在类的注释写可,大概意思是这是个简单的秒表,用于在开发过程中方便程序员调试性能等,非线程安全,不建议用于生产。configureHeadlessProperty 设置使用 Headless,对于只有远程登录使用的服务器来说这样性能要好一些。接着是加载用于这个 run 方法启动过程的监听器,依然是 getSpringFactoriesInstances 方法,这次的类型是 org.springframework.boot.SpringApplicationRunListener:
# Run Listeners org.springframework.boot.SpringApplicationRunListener=\ org.springframework.boot.context.event.EventPublishingRunListener
SpringApplicationRunListeners(Log log, Collection<? extends SpringApplicationRunListener> listeners) { this.log = log; this.listeners = new ArrayList<SpringApplicationRunListener>(listeners); }
先是加载所有可用监听,然后初始化 SpringApplicationRunListeners 对象,最后循环启动所有 SpringApplicationRunListener 监听。启动监听的方法:
@Override public void started() { this.initialMulticaster .multicastEvent(new ApplicationStartedEvent(this.application, this.args));}
ApplicationStartedEvent 实例化传了两个参数,先看第一个参数 this.application 是怎么来的,实例的 SpringApplication 的 run 方法中,用于获取 SpringApplicationRunListener,也就是前面说的 getSpringFactoriesInstances 被调用时:
private SpringApplicationRunListeners getRunListeners(String[] args) { Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class }; return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances( SpringApplicationRunListener.class, types, this, args));}
getSpringFactoriesInstances 方法的参数包含 SpringApplication.class 和 this,这两个参数被传入 createSpringFactoriesInstances 方法:
可以看到,是通过反射创建实例的时候,将 SpringApplication 中的 this 传进来 EventPublishingRunListener 构造的,然后 EventPublishingRunListener 构造:
public EventPublishingRunListener(SpringApplication application, String[] args) { this.application = application; this.args = args; this.initialMulticaster = new SimpleApplicationEventMulticaster(); for (ApplicationListener<?> listener : application.getListeners()) { this.initialMulticaster.addApplicationListener(listener); } }
最后在构造 ApplicationStartedEvent 时传给它的基类 EventObject 的 protected 不可序列化属性 source。实例化 ApplicationStartedEvent 后 instance.getClass() 并包装为 ResolvableType 类型以保存类型信息,并将它和 event 作为参数传入 SimpleApplicationEventMulticaster 的 multicastEvent 方法。multicastEvent 首先获取 ApplicationListener,使用 getApplicationListeners 方法,这个方法中抛开对 listener 做了一些缓存类工作外,主要就是将事件和对应的监听器做了下是否支持的验证,返回通过了 retrieveApplicationListeners 中通过了 supportsEvent 验证的监听器集合,这里就体现出了 ResolvableType 的作用,它保存了类型的信息同时对泛型类型也支持。
得到了这些匹配的监听器后,判断当前 Executor 是否被设置过,如果为 null 则同步循环执行所有:invokeListener(listener, event); 如果不为 null 则:
executor.execute(new Runnable() { @Override public void run() {invokeListener(listener, event); } });
监听器执行的时候也会先判断是否是该由自己处理的事件,例如:
@Override public void onApplicationEvent(ApplicationEvent event) { if (event instanceof ApplicationEnvironmentPreparedEvent) { onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);} if (event instanceof ApplicationPreparedEvent) {onApplicationPreparedEvent(event); } }
监听启动后,只准备一些启动参数,和环境变量 prepareEnvironment 方法先是读取了应用的启动参数和 profile 配置,然后用 listeners.environmentPrepared(environment) 传给监听器:
public void environmentPrepared(ConfigurableEnvironment environment) { this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent( this.application, this.args, environment));}
接着判断如果 environment 是 org.springframework.web.context.ConfigurableWebEnvironment 的实例,但 webEnvironment 不是 true,也就是说存在 org.springframework.web.context.ConfigurableWebEnvironmen 但不存在 javax.servlet.Servlet 的情况,会多执行一步 environment = convertToStandardEnvironment(environment) 转换。
之后的 printBanner 就不细说了,如果你在 resource 下自定义了一个 banner.txt 文件,启动时会输出内容,否则输出:
. ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ (()\___ | '_ |'_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ))) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v1.4.3.RELEASE)
接着创建 ConfigurableApplicationContext 实例,方法也很简单,如果是 web 环境就 BeanUtils.instantiate 一个 org.springframework.boot.context.embedded. AnnotationConfigEmbeddedWebApplicationContext 的实例并强转为 ConfigurableApplicationContext,否则用 org.springframework.context.annotation. AnnotationConfigApplicationContext 的实例强转。
创建 FailureAnalyzers 实例,记录了 ConfigurableApplicationContext 实例中需要关注的部分,如果启动出错了可以据此分析,可以配置,具体的逻辑依然是老方法 spring.factories:
不同的 Analyzer 关注不同的部分,自己可以扩展配置,最后 prepareFailureAnalyzers 方法给所有 Analyzer 实例 setBeanFactory(context.getBeanFactory()),一旦启动过程进入 catch,被注册的 Analyzer 实例的 analyze 方法就会被触发执行,分析结果会被 loggedExceptions.add(exception) 加入到抛出的异常中:
private FailureAnalysis analyze(Throwable failure, List<FailureAnalyzer> analyzers) { for (FailureAnalyzer analyzer : analyzers) { FailureAnalysis analysis = analyzer.analyze(failure); if (analysis != null) { return analysis; } } return null; }例如:NoSuchBeanDefinitionFailureAnalyzer
@Override
protected FailureAnalysis analyze(Throwable rootFailure,
NoSuchBeanDefinitionException cause, String description) {
if (cause.getNumberOfBeansFound() != 0) {
return null;
}
List<AutoConfigurationResult> autoConfigurationResults = getAutoConfigurationResults(
cause);
StringBuilder message = new StringBuilder();
message.append(String.format("%s required %s that could not be found.%n",
description == null ? "A component" : description,
getBeanDescription(cause)));
if (!autoConfigurationResults.isEmpty()) {
for (AutoConfigurationResult provider : autoConfigurationResults) {
message.append(String.format("\t- %s%n", provider));
}
}
String action = String.format("Consider %s %s in your configuration.",
(!autoConfigurationResults.isEmpty()
? "revisiting the conditions above or defining" : "defining"),
getBeanDescription(cause));
return new FailureAnalysis(message.toString(), action, cause);
}
prepareContext 方法中 postProcessApplicationContext 会在 this.beanNameGenerator 存在的情况下加载自定义命名策略,然后在 this.resourceLoader 存在的情况下为 context 设置 resourceLoader 和 classLoader。applyInitializers 方法调用之前加载的 Initializer 的实例并执行其 initialize 方法,例如加载环境变量信息、注册 EmbeddedServletContainerInitializedEvent 的监听、注册 CachingMetadataReaderFactoryPostProcessor 等。listeners.contextPrepared(context) 由于 EventPublishingRunListener 的 contextPrepared 是空的,先不说了。logStartupInfo 部分初始化了 logger,然后根据配置情况打印了启动或运行以及 profile 是否配置的日志:
protected void logStartupInfo(boolean isRoot) { if (isRoot) { new StartupInfoLogger(this.mainApplicationClass).logStarting(getApplicationLog()); } }</span><span style="color: rgba(0, 0, 255, 1)">protected</span><span style="color: rgba(0, 0, 0, 1)"> Log getApplicationLog() { </span><span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(0, 0, 255, 1)">this</span>.mainApplicationClass == <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">) { </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> logger; } </span><span style="color: rgba(0, 0, 255, 1)">return</span> LogFactory.getLog(<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.mainApplicationClass); } </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> logStarting(Log log) { Assert.notNull(log, </span>"Log must not be null"<span style="color: rgba(0, 0, 0, 1)">); </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (log.isInfoEnabled()) { log.info(getStartupMessage()); } </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (log.isDebugEnabled()) { log.debug(getRunningMessage()); } } </span><span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> logStartupProfileInfo(ConfigurableApplicationContext context) { Log log </span>=<span style="color: rgba(0, 0, 0, 1)"> getApplicationLog(); </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (log.isInfoEnabled()) { String[] activeProfiles </span>=<span style="color: rgba(0, 0, 0, 1)"> context.getEnvironment().getActiveProfiles(); </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (ObjectUtils.isEmpty(activeProfiles)) { String[] defaultProfiles </span>=<span style="color: rgba(0, 0, 0, 1)"> context.getEnvironment().getDefaultProfiles(); log.info(</span>"No active profile set, falling back to default profiles: " +<span style="color: rgba(0, 0, 0, 1)"> StringUtils.arrayToCommaDelimitedString(defaultProfiles)); } </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> { log.info(</span>"The following profiles are active: " +<span style="color: rgba(0, 0, 0, 1)"> StringUtils.arrayToCommaDelimitedString(activeProfiles)); } } }</span></pre>
接着 prepareContext 中注册启动参数(applicationArguments)到 bean 工厂,包括 logger、commandLineArgs 等。然后加载 bean 定义的来源并根据其中配置加载 bean,这里的 sources 就是初始化启动类时传进来的那个 sources:
BeanDefinitionLoader(BeanDefinitionRegistry registry, Object... sources) { Assert.notNull(registry, "Registry must not be null"); Assert.notEmpty(sources, "Sources must not be empty"); this.sources = sources; this.annotatedReader = new AnnotatedBeanDefinitionReader(registry); this.xmlReader = new XmlBeanDefinitionReader(registry); if (isGroovyPresent()) { this.groovyReader = new GroovyBeanDefinitionReader(registry); } this.scanner = new ClassPathBeanDefinitionScanner(registry); this.scanner.addExcludeFilter(new ClassExcludeFilter(sources)); }
注意下面的 sources 是待加载的,和上面这段不是同一个:
public int load() { int count = 0; for (Object source : this.sources) { count += load(source); } return count; }</span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> load(Object source) { Assert.notNull(source, </span>"Source must not be null"<span style="color: rgba(0, 0, 0, 1)">); </span><span style="color: rgba(0, 0, 255, 1)">if</span> (source <span style="color: rgba(0, 0, 255, 1)">instanceof</span> Class<?><span style="color: rgba(0, 0, 0, 1)">) { </span><span style="color: rgba(0, 0, 255, 1)">return</span> load((Class<?><span style="color: rgba(0, 0, 0, 1)">) source); } </span><span style="color: rgba(0, 0, 255, 1)">if</span> (source <span style="color: rgba(0, 0, 255, 1)">instanceof</span><span style="color: rgba(0, 0, 0, 1)"> Resource) { </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> load((Resource) source); } </span><span style="color: rgba(0, 0, 255, 1)">if</span> (source <span style="color: rgba(0, 0, 255, 1)">instanceof</span><span style="color: rgba(0, 0, 0, 1)"> Package) { </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> load((Package) source); } </span><span style="color: rgba(0, 0, 255, 1)">if</span> (source <span style="color: rgba(0, 0, 255, 1)">instanceof</span><span style="color: rgba(0, 0, 0, 1)"> CharSequence) { </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> load((CharSequence) source); } </span><span style="color: rgba(0, 0, 255, 1)">throw</span> <span style="color: rgba(0, 0, 255, 1)">new</span> IllegalArgumentException("Invalid source type " +<span style="color: rgba(0, 0, 0, 1)"> source.getClass()); }</span></pre>
类型不同加载过程不同,其中 Class<?> 加载过程大概是通过 BeanDefinitionLoader 调用 AnnotatedBeanDefinitionReader 的 registerBean 方法:
public void registerBean(Class<?> annotatedClass, String name, Class<? extends Annotation>... qualifiers) { AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass); if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) { return; }ScopeMetadata scopeMetadata </span>= <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.scopeMetadataResolver.resolveScopeMetadata(abd); abd.setScope(scopeMetadata.getScopeName()); String beanName </span>= (name != <span style="color: rgba(0, 0, 255, 1)">null</span> ? name : <span style="color: rgba(0, 0, 255, 1)">this</span>.beanNameGenerator.generateBeanName(abd, <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.registry)); AnnotationConfigUtils.processCommonDefinitionAnnotations(abd); </span><span style="color: rgba(0, 0, 255, 1)">if</span> (qualifiers != <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">) { </span><span style="color: rgba(0, 0, 255, 1)">for</span> (Class<? <span style="color: rgba(0, 0, 255, 1)">extends</span> Annotation><span style="color: rgba(0, 0, 0, 1)"> qualifier : qualifiers) { </span><span style="color: rgba(0, 0, 255, 1)">if</span> (Primary.<span style="color: rgba(0, 0, 255, 1)">class</span> ==<span style="color: rgba(0, 0, 0, 1)"> qualifier) { abd.setPrimary(</span><span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">); } </span><span style="color: rgba(0, 0, 255, 1)">else</span> <span style="color: rgba(0, 0, 255, 1)">if</span> (Lazy.<span style="color: rgba(0, 0, 255, 1)">class</span> ==<span style="color: rgba(0, 0, 0, 1)"> qualifier) { abd.setLazyInit(</span><span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">); } </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> { abd.addQualifier(</span><span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> AutowireCandidateQualifier(qualifier)); } } } BeanDefinitionHolder definitionHolder </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> BeanDefinitionHolder(abd, beanName); definitionHolder </span>= AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.registry); BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, </span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.registry); }</span></pre>
可以看到有生成方法名,设置默认注入的实例、延迟以及过滤等等,注入的过程包括初始化一些信息,如构造、内部类、注解等:
protected AbstractBeanDefinition(ConstructorArgumentValues cargs, MutablePropertyValues pvs) {setConstructorArgumentValues(cargs); setPropertyValues(pvs); }</span><span style="color: rgba(0, 0, 255, 1)">public</span> StandardAnnotationMetadata(Class<?> introspectedClass, <span style="color: rgba(0, 0, 255, 1)">boolean</span><span style="color: rgba(0, 0, 0, 1)"> nestedAnnotationsAsMap) { </span><span style="color: rgba(0, 0, 255, 1)">super</span><span style="color: rgba(0, 0, 0, 1)">(introspectedClass); </span><span style="color: rgba(0, 0, 255, 1)">this</span>.annotations =<span style="color: rgba(0, 0, 0, 1)"> introspectedClass.getAnnotations(); </span><span style="color: rgba(0, 0, 255, 1)">this</span>.nestedAnnotationsAsMap =<span style="color: rgba(0, 0, 0, 1)"> nestedAnnotationsAsMap; }</span></pre>
其他三种比如有的有输入流什么的就不细总结了,这部分介绍 Spring IOC 的相关文章应该不少。
prepareContext 方法最后 listeners.contextLoaded(context),加载监听器到 context 并广播 ApplicationPreparedEvent 事件。
==========================================================
咱最近用的 github:https://github.com/saaavsaaa
微信公众号: