通过代码实例了解SpringBoot启动原理

网友投稿 345 2022-12-18


通过代码实例了解SpringBoot启动原理

这篇文章主要介绍了通过代码实例了解SpringBoot启动原理,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

SpringBoot和Spring相比,有着不少优势,比如自动配置,jar直接运行等等。那么SpringBoot到底是怎么启动的呢?

下面是SpringBoot启动的入口:

@SpringBootApplication

public class HelloApplication {

public static void main(String[] args) {

SpringApplication.run(HelloApplication.class, args);

}

}

一、先看一下@SpringBoot注解:

@Target({ElementType.TYPE}) //定义其使用时机

@Retention(RetentionPolicy.RUNTIME) //编译程序将Annotation储存于class档中,可由VM使用反射机制的代码所读取和使用。

@Documented //这个注解应该被 javadoc工具记录

@Inherited //被注解的类会自动继承. 更具体地说,如果定义注解时使用了 @Inherited 标记,然后用定义的注解来标注另一个父类, 父类又有一个子类(subclass),则父类的所有属性将被继承到它的子类中.

@SpringBootConfiguration //@SpringBootConfiguration就相当于@Configuration。JavaConfig配置形式

@EnableAutoConfiguration

@ComponentScan(

excludeFilters = {@Filter(

type = FilterType.CUSTOM,

classes = {TypeExcludeFilter.class}

), @Filter(

type = FilterType.CUSTOM,

classes = {AutoConfigurationExcludeFilter.class}

)} //自动扫描并加载符合条件的组件。以通过basePackages等属性来细粒度的定制@ComponentScan自动扫描的范围,如果不指定,则默认Spring框架实现会从声明@ComponentScan所在类的package进行扫描。

注:所以SpringBoot的启动类最好是放在root package下,因为默认不指定basePackages。

)

public @interface SpringBootApplication {

@AliasFor(

annotation = EnableAutoConfiguration.class

)

Class>[] exclude() default {};

@AliasFor(

annotation = EnableAutoConfiguration.class

)

String[] excludeName() default {};

@AliasFor(

annotation = ComponentScan.class,

attribute = "basePackages"

)

String[] scanBasePackages() default {};

@AliasFor(

annotation = ComponentScan.class,

attribute = "basePackageClasses"

)

Class>[] scanBasePackageClasses() default {};

}

所以,实际上SpringBootApplication注解相当于三个注解的组合,@SpringBootConfiguration,@ComponentScan和@EnableAutoConfiguration。

@SpringBootConfiguration和@ComponentScan,很容易知道它的意思,一个是JavaConfig配置,一个是扫描包。关键在于@EnableAutoConfiguration注解。先来看一下这个注解:

@Target({ElementType.TYPE})

@Retention(RetentionPolicy.RUNTIME)

@Documented

@Inherited

@AutoConfigurationPackage

@Import({AutoConfigurationImportSelector.class})

public @interface EnableAutoConfiguration {

String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

Class>[] exclude() default {};

String[] excludeName() default {};

}

Springboot应用启动过程中使用ConfigurationClassParser分析配置类时,如果发现注解中存在@Import(ImportSelector)的情况,就会创建一个相应的ImportSelector对象, 并调用其方法 public String[] selectImports(AnnotationMetadata annotationMetadata), 这里 EnableAutoConfigurationImportSelector的导入@Import(EnableAutoConfigurationImportSelector.class) 就属于这种情况,所以ConfigurationClassParser会实例化一个 EnableAutoConfigurationImportSelector 并调用它的 selectImports() 方法。

AutoConfigurationImportSelector implements DeferredImportSelector extends ImportSelector。

下面是AutoConfigurationImportSelector的执行过程:

public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {

private static final String[] NO_IMPORTS = new String[0];

private static final Log logger = LogFactory.getLog(AutoConfigurationImportSelector.class);

private static final String PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE = "spring.autoconfigure.exclude";

private ConfigurableListableBeanFactory beanFactory;

private Environment environment;

private ClassLoader beanClassLoader;

private ResourceLoader resourceLoader;

public AutoConfigurationImportSelector() {

}

public String[] selectImports(AnnotationMetadata annotationMetadata) {

if (!this.isEnabled(annotationMetadata)) {

return NO_IMPORTS;

} else {

// 从配置文件中加载 AutoConfigurationMetadata

AutoConfigumMdEpdAHmsrationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);

AnnotationAttributes attributes = this.getAttributes(annotationMetadata);

// 获取所有候选配置类EnableAutoConfiguration

// 使用了内部工具使用SpringFactoriesLoader,查找classpath上所有jar包中的

// META-INF\spring.factories,找出其中key为

// org.springframework.boot.autoconfigure.EnableAutoConfiguration

// 的属性定义的工厂类名称。

// 虽然参数有annotationMetadata,attributes,但在 AutoConfigurationImportSelector 的

// 实现 getCandidateConfigurations()中,这两个参数并未使用

List configurations = this.getCandidateConfigurations(annotationMetadata, attributes);

//去重

configurations = this.removeDuplicates(configurations);

Set exclusions = this.getExcluhttp://sions(annotationMetadata, attributes);

// 应用 exclusion 属性

this.checkExcludedClasses(configurations, exclusions);

configurations.removeAll(exclusions);

// 应用过滤器AutoConfigurationImportFilter,

// 对于 spring boot autoconfigure,定义了一个需要被应用的过滤器 :

// org.springframework.boot.autoconfigure.condition.OnClassCondition,

// 此过滤器检查候选配置类上的注解@ConditionalOnClass,如果要求的类在classpath

// 中不存在,则这个候选配置类会被排除掉

configurations = this.filter(configurations, autoConfigurationMetadata);

// 现在已经找到所有需要被应用的候选配置类

// 广播事件 AutoConfigurationImportEvent

this.fireAutoConfigurationImportEvents(configurations, exclusions);

return StringUtils.toStringArray(configurations);

}

}

protected AnnotationAttributes getAttributes(AnnotationMetadata metadata) {

String name = this.getAnnotationClass().getName();

AnnotationAttributes attributes = AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(name, true));

Assert.notNull(attributes, () -> {

return "No auto-configuration attributes found. Is " + metadata.getClassName() + " annotated with " + ClassUtils.getShortName(name) + "?";

});

return attributes;

}

protected List getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {

List configurations = SpringFahttp://ctoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLhttp://oader());

Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");

return configurations;

}

public abstract class SpringFactoriesLoader {

public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

private static final Log logger = LogFactory.getLog(SpringFactoriesLoader.class);

private static final Map> cache = new ConcurrentReferenceHashMap();

public SpringFactoriesLoader() {

}

public static List loadFactoryNames(Class> factoryClass, @Nullable ClassLoader classLoader) {

String factoryClassName = factoryClass.getName();

return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());

}

}

private List filter(List configurations, AutoConfigurationMetadata autoConfigurationMetadata) {

long startTime = System.nanoTime();

String[] candidates = StringUtils.toStringArray(configurations);

// 记录候选配置类是否需要被排除,skip为true表示需要被排除,全部初始化为false,不需要被排除

boolean[] skip = new boolean[candidates.length];

// 记录候选配置类中是否有任何一个候选配置类被忽略,初始化为false

boolean skipped = false;

Iterator var8 = this.getAutoConfigurationImportFilters().iterator();

// 获取AutoConfigurationImportFilter并逐个应用过滤

while(var8.hasNext()) {

AutoConfigurationImportFilter filter = (AutoConfigurationImportFilter)var8.next();

// 对过滤器注入其需要Aware的信息

this.invokeAwareMethods(filter);

// 使用此过滤器检查候选配置类跟autoConfigurationMetadata的匹配情况

boolean[] match = filter.match(candidates, autoConfigurationMetadata);

for(int i = 0; i < match.length; ++i) {

if (!match[i]) {

// 如果有某个候选配置类不符合当前过滤器,将其标记为需要被排除,

// 并且将 skipped设置为true,表示发现了某个候选配置类需要被排除

skip[i] = true;

skipped = true;

}

}

}

if (!skipped) {

// 如果所有的候选配置类都不需要被排除,则直接返回外部参数提供的候选配置类集合

return configurations;

} else {

// 逻辑走到这里因为skipped为true,表明上面的的过滤器应用逻辑中发现了某些候选配置类

// 需要被排除,这里排除那些需要被排除的候选配置类,将那些不需要被排除的候选配置类组成

// 一个新的集合返回给调用者

List result = new ArrayList(candidates.length);

int numberFiltered;

for(numberFiltered = 0; numberFiltered < candidates.length; ++numberFiltered) {

if (!skip[numberFiltered]) {

result.add(candidates[numberFiltered]);

}

}

if (logger.isTraceEnabled()) {

numberFiltered = configurations.size() - result.size();

logger.trace("Filtered " + numberFiltered + " auto configuration class in " + TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime) + " ms");

}

return new ArrayList(result);

}

}

/**

* 使用内部工具 SpringFactoriesLoader,查找classpath上所有jar包中的

* META-INF\spring.factories,找出其中key为

* org.springframework.boot.autoconfigure.AutoConfigurationImportFilter

* 的属性定义的过滤器类并实例化。

* AutoConfigurationImportFilter过滤器可以被注册到 spring.factories用于对自动配置类

* 做一些限制,在这些自动配置类的字节码被读取之前做快速排除处理。

* spring boot autoconfigure 缺省注册了一个 AutoConfigurationImportFilter :

* org.springframework.boot.autoconfigure.condition.OnClassCondition

**/

protected List getAutoConfigurationImportFilters() {

return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class, this.beanClassLoader);

}

二、下面看一下SpringBoot启动时run方法执行过程

public ConfigurableApplicationContext run(String... args) {

StopWatch stopWatch = new StopWatch();

stopWatch.start();

ConfigurableApplicationContext context = null;

Collection exceptionReporters = new ArrayList<>();

this.configureHeadlessProperty();

//从类路径下META-INF/spring.factories获取

SpringApplicationRunListeners listeners = getRunListeners(args);

//回调所有的获取SpringApplicationRunListener.starting()方法

listeners.starting();

try {

//封装命令行参数

ApplicationArguments applicationArguments = new DefaultApplicationArguments(

args);

//准备环境

ConfigurableEnvironment environment = prepareEnvironment(listeners,

applicationArguments);

//创建环境完成后回调

SpringApplicationRunListener.environmentPrepared();表示环境准备完成

this.configureIgnoreBeanInfo(environment);

//打印Banner图

Banner printedBanner = printBanner(environment);

//创建ApplicationContext,决定创建web的ioc还是普通的ioc

context = createApplicationContext();

//异常分析报告

exceptionReporters = getSpringFactoriesInstances(

SpringBootExceptionReporter.class,

new Class[] { ConfigurableApplicationContext.class }, context);

//准备上下文环境,将environment保存到ioc中

//applyInitializers():回调之前保存的所有的ApplicationContextInitializer的initialize方法

//listeners.contextPrepared(context)

//prepareContext运行完成以后回调所有的SpringApplicationRunListener的contextLoaded()

this.prepareContext(context, environment, listeners, applicationArguments,

printedBanner);

//刷新容器,ioc容器初始化(如果是web应用还会创建嵌入式的Tomcat)

//扫描,创建,加载所有组件的地方,(配置类,组件,自动配置)

this.refreshContext(context);

this.afterRefresh(context, applicationArguments);

stopWatch.stop();

if (this.logStartupInfo) {

new StartupInfoLogger(this.mainApplicationClass)

.logStarted(getApplicationLog(), stopWatch);

}

//所有的SpringApplicationRunListener回调started方法

listeners.started(context);

//从ioc容器中获取所有的ApplicationRunner和CommandLineRunner进行回调,

//ApplicationRunner先回调,CommandLineRunner再回调

this.callRunners(context, applicationArguments);

}

catch (Throwable ex) {

this.handleRunFailure(context, ex, exceptionReporters, listeners);

throw new IllegalStateException(ex);

}

try {

//所有的SpringApplicationRunListener回调running方法

listeners.running(context);

}

catch (Throwable ex) {

this.handleRunFailure(context, ex, exceptionReporters, null);

throw new IllegalStateException(ex);

}

//整个SpringBoot应用启动完成以后返回启动的ioc容器

return context;

}


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

上一篇:Java简单数据加密方法DES实现过程解析
下一篇:springboot单元测试两种方法实例详解
相关文章

 发表评论

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