通过代码实例了解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 AutoConfigurationMetadata 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<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes); //去重 configurations = this.removeDuplicates(configurations); Set<String> exclusions = this.getExclusions(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<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader()); 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<ClassLoader, MultiValueMap<String, String>> cache = new ConcurrentReferenceHashMap(); public SpringFactoriesLoader() { } public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) { String factoryClassName = factoryClass.getName(); return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList()); } } private List<String> filter(List<String> 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<String> 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<AutoConfigurationImportFilter> 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<SpringBootExceptionReporter> 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; }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。
栏 目:Java
本文地址:https://www.xiuzhanwang.com/a1/Java/8893.html
您可能感兴趣的文章
- 01-10Java实现动态模拟时钟
- 01-10java基于poi导出excel透视表代码实例
- 01-10Java实现动态数字时钟
- 01-10基于Java验证jwt token代码实例
- 01-10使用Swing绘制动态时钟
- 01-10Java实现双保险线程的示例代码
- 01-10springboot集成fastDfs过程代码实例
- 01-10Java内存模型原子性原理及实例解析
- 01-10springboot 配置DRUID数据源的方法实例分析
- 01-10Java包装类的缓存机制原理实例详解
阅读排行
本栏相关
- 01-10Java实现动态模拟时钟
- 01-10Springboot中@Value的使用详解
- 01-10JavaWeb实现邮件发送功能
- 01-10利用Java实现复制Excel工作表功能
- 01-10Java实现动态数字时钟
- 01-10java基于poi导出excel透视表代码实例
- 01-10java实现液晶数字字体显示当前时间
- 01-10基于Java验证jwt token代码实例
- 01-10Java动态显示当前日期和时间
- 01-10浅谈Java中真的只有值传递么
随机阅读
- 08-05织梦dedecms什么时候用栏目交叉功能?
- 08-05DEDE织梦data目录下的sessions文件夹有什
- 01-10SublimeText编译C开发环境设置
- 08-05dedecms(织梦)副栏目数量限制代码修改
- 01-11ajax实现页面的局部加载
- 01-10使用C语言求解扑克牌的顺子及n个骰子
- 01-11Mac OSX 打开原生自带读写NTFS功能(图文
- 04-02jquery与jsp,用jquery
- 01-10delphi制作wav文件的方法
- 01-10C#中split用法实例总结