Spring Boot启动过程完全解析(一)

网友投稿 244 2023-05-21


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>类型变量initializers:

public void setInitializers(

Collection extends ApplicationContextInitializer>> initializers) {

this.initializers = new ArrayList>();

this.initializers.addAll(initializers);

}

设置的时候会先new,也就是说这方法每次都是整体更换,不会追加。这个方法的参数都是各个模块中配置在META-INF/spring.factories中的key为org.springframework.context.ApplicationContextInitializer的值,这些类都是接口ApplicationContextInitializer的泛型实现。

private Collection extends T> getSpringFactoriesInstances(Class type,

Class>[] parameterTypes, Object... args) {

ClassLoader classLoader = Thread.currentThread().getContextClassLoader();

// Use names and ensure unique to protect against duplicates

Set names = new LinkedHashSet(

SpringFactoriesLoader.loadFactoryNames(type, classLoader));

List instances = createSpringFactoriesInstances(type, parameterTypes,

classLoader, args, names);

AnnotationAwareOrderComparator.sort(instances);

return instances;

}

使用SpringFactoriesLoader.loadFactoryNames方法去取上面说的被配置的ApplicationContextInitializer的名字放进Set中,并用反射创建这些名字的实例。

setInitializers方法之后又是setInitializers,参数同上都是getSpringFactoriesInstances方法获取,只不过这次参数Class 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(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 Simplhttp://eApplicationEventMulticaster();

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 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 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());

}

}

protected Log getApplicationLog() {

if (this.mainApplicationClass == null) {

return logger;

}

return LogFactory.getLog(this.mainApplicationClass);

}

public void logStarting(Log log) {

Assert.notNull(log, "Log must not be null");

if (log.isInfoEnabled()) {

log.info(getStartupMessage());

}

if (log.isDebugEnabled()) {

log.debug(getRunningMessage());

}

}

protected void logStartupProfileInfo(ConfigurableApplicationContext context) {

Log log = getApplicationLog();

if (log.isInfoEnabled()) {

String[] activeProfiles = context.getEnvironment().getActiveProfiles();

if (ObjectUtils.isEmpty(activeProfiles)) {

String[] defaultProfiles = context.getEnvironment().getDefaultProfiles();

log.info("No active profile set, falling back to default profiles: "

+ StringUtils.arrayToCommaDelimitedString(defaultProfiles));

}

else {

log.info("The following profiles are active: "

+ StringUtils.arrayToCommaDelimitedString(activeProfiles));

}

}

}

接着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;

}

private int load(Object source) {

Assert.notNull(source, "Source must not be null");

if (source instanceof Class>) {

return load((Class>) source);

}

if (source instanceof Resource) {

return load((Resource) source);

}

if (source instanceof Package) {

return load((Package) source);

}

if (source instanceof CharSequence) {

return load((CharSequence) source);

}

throw new IllegalArgumentException("Invalid source type " + source.getClass());

}

类型不同加载过程不同,其中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 = this.scopeMetadataResolver.resolveScopeMetadata(abd);

abd.setScope(scopeMetadata.getScopeName());

String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));

AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);

if (qualifiers != null) {

for (Class extends Annotation> qualifier : qualifiers) {

if (Primary.class == qualifier) {

abd.setPrimary(true);

}

else if (Lazy.class == qualifier) {

abd.setLazyInit(true);

}

else {

abd.addQualifier(new AutowireCandidateQualifier(qualifier));

}

}

}

BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);

definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);

BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);

}

可以看到有生成方法名,设置默认注入的实例、延迟以及过滤等等,注入的过程包括初始化一些信息,如构造、内部类、注解等:

protected AbstractBeanDefinition(ConstructorArgumentValues cargs, MutablePropertyValues pvs) {

setConstructorArgumentValues(cargs);

setPropertyValues(pvs);

}

public StandardAnnotationMetadata(Class> introspectedClass, boolean nestedAnnotationsAsMap) {

super(introspectedClass);

this.annotations = introspectedClass.getAnnotations();

this.nestedAnnotationsAsMap = nestedAnnotationsAsMap;

}

其他三种比如有的有输入流什么的就不细总结了,这部分介绍Spring IOC的相关文章应该不少。

prepareContext方法最后listeners.contextLoaded(context),加载监听器到context并广播ApplicationPreparedEvent事件。

咱最近用的github:https://github.com/saaavsaaa

以上所述是给大家介绍的Spring Boot启动过程完全解析(一),希望对大家有所帮助,如果大家有任何疑问请给我留言,会及时回复大家的。在此也非常感谢大家对我们网站的支持!


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:uploader秒传图片到服务器完整代码
下一篇:使用Xcache缓存器加速PHP网站的配置方法
相关文章

 发表评论

暂时没有评论,来抢沙发吧~