Springboot介绍
Springboot并不是一个新的功能框架,而是Spring的一个子项目框架,它提供了自动装配的功能,简化了使用Spring框架的各种繁琐配置,而且集成了大部分主流组件配置.做到了Spring的开箱即用.
Springboot启动
Springboot启动离不开@SpringBootApplication注解,它为SpringApplication上下文的各类Bean加载提供了很大的支持
@SpringBootApplication注解
@SpringBootApplication注解在Springboot项目启动类上,是Springboot启动非常重要的部分.它主要由三个核心注解集成,为其提供核心功能该注解加载Bean至容器中发生于run方法的上下文准备完成后的刷新上下文过程中
(this.refreshContext(context))
1. @SpringBootConfiguration
该注解集成@Configuration,表示该类为配置类,它也会被加载至容器中进行管理
2. @EnableAutoConfiguration(开启Springboot自动装配机制)
- 该类由@AutoConfigurationPackage和@Import({AutoConfigurationImportSelector.class})集成
- @AutoConfigurationPackage注解又集成了@Import({Registrar.class})注解,它负责加载当前包路径下所有的@Configuration配置Bean.该注解加载Bean至容器中发生于run方法的上下文准备完成后的刷新上下文过程中
(this.refreshContext(context))
//它是AutoConfigurationPackages抽象类的内部类 static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports { Registrar() { } //注册Bean方法 public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { //这一步的getPackageNames()方法获取当前包路径用以进行自动包配置Bean的注册.它是拿的当前注解的一些元数据包括当前的包名 AutoConfigurationPackages.register(registry, (String[])(new AutoConfigurationPackages.PackageImports(metadata)).getPackageNames().toArray(new String[0])); } public Set<Object> determineImports(AnnotationMetadata metadata) { return Collections.singleton(new AutoConfigurationPackages.PackageImports(metadata)); } }
- @Import({AutoConfigurationImportSelector.class})其加载了依赖的各个组件resource下/MEAT-INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration
指定的所有配置Bean.该注解加载Bean至容器中发生于run方法的上下文准备完成后的刷新上下文过程中(this.refreshContext(context))
. 可参考spring-boot-autoconfigure依赖下的spring.factories文件.
protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) { if (!this.isEnabled(annotationMetadata)) { return EMPTY_ENTRY; } else { AnnotationAttributes attributes = this.getAttributes(annotationMetadata); // 获取所有配置文件中配置的配置类名 List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes); configurations = this.removeDuplicates(configurations); Set<String> exclusions = this.getExclusions(annotationMetadata, attributes); this.checkExcludedClasses(configurations, exclusions); configurations.removeAll(exclusions); configurations = this.getConfigurationClassFilter().filter(configurations); this.fireAutoConfigurationImportEvents(configurations, exclusions); return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions); } } protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { // 从spring.factories文件中获取EnableAutoConfiguration键指定的自动配置类列表(需加@Configuration注解). SpringFactoriesLoader.loadFactoryNames方法将在Springboot启动过程中多次用到 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; } protected Class<?> getSpringFactoriesLoaderFactoryClass() { return EnableAutoConfiguration.class; }
3. @ComponentScan
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM,classes ={TypeExcludeFilter.class}), @Filter(type = FilterType.CUSTOM,classes = {AutoConfigurationExcludeFilter.class}) })
该注解主要用来扫描@Component及其集成它的类装配到容器中(默认启动类包路径下).例如@Controller、@Service、@Repository等Bean声明注解.
SpringApplication启动
==讲完@SpringBootApplication注解就要讲启动了,其启动又分为实例化和run方法执行==
1. SpringApplication类实例化
==传入基类构造SpringApplication实例==
- 保存基类至SpringApplication基类属性(PrimarySources集合)中
- 设置该应用类型是响应式
REACTIVE
的还是Web应用SERVLET
(判断有没有DispatcherServlet )- 加载上下文初始化器ApplicationContextInitializer集合 ---> 从spring.factories中读取设置全局启动上下文初始化器(实现ApplicationContextInitializer.class类的)
- 加载监听器ApplicationListener集合 ---> 从spring.factories中读取设置的监听器(实现ApplicationListener.class类的)
上面的设置用到下面方法 setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class)) setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)) 主要使用SpringFactoriesLoader.loadFactoryNames(Class<?> factoryType, ClassLoader classLoader)方法,通过读取spring.factories中设置的符合定义类名集合来进行加载实例化
SpringApplication.run(String ..args)方法构造Application上下文
该run方法内部主要实现了运行环境配置以及SpringApplication上下文的装载准备.并在不同的阶段发布不同的事件以进行服务启动前的一些配置准备工作
- 获取SpringApplicationRunListeners监听器列表(默认有EventPublishingRunListener,在启动不同阶段发布不同的启动事件).可在spring.factories添加自定义启动监听器(实现
SpringApplicationRunListener.class
类).- 执行监听器列表的starting方法,默认发布启动事件
listeners.starting()
(ApplicationStartingEvent
)- 初始化应用环境,并且监听器列表发布环境准备完毕事件
listeners.environmentPrepared()
(ApplicationEnvironmentPreparedEvent
),并且在这一步读取了bootstrap和application配置文件配置- 实例化并打印Banner
- 创建应用上下文
ConfigurableApplicationContext
,为上下文配置环境参数、执行初始化器.发布上下文初始化完毕事件listeners.contextPrepared(context)
(ApplicationContextInitializedEvent
).- 给Bean工厂加入一些启动过程中产生的单例Bean,设置上下文Bean是否懒加载,加入根启动类Bean.发布上下文准备完毕事件
listeners.contextLoaded(context)
(ApplicationPreparedEvent
,这一步也进行了bootstrap和application配置读取)- 刷新上下文
refreshContext(context)
,执行大名鼎鼎的refresh()方法- 执行刷新后处理方法afterRefresh,这一步啥都没有做,方法体是空的,可以继承自定义
- 监听器发布启动完毕监听事件
listeners.started(context)
(ApplicationStartedEvent
)- 执行配置的ApplicationRunner和CommandLineRunner(可实现该两个接口并使用注解注入Bean即可.此时上下文已经加载好了.只在这时执行一次)
- 最后发布运行事件
listeners.running(context)
(ApplicationReadyEvent
)
上述事件的监听器自定义可通过spring.factoriesorg.springframework.context.ApplicationListener= 指定自定义的监听器全路径地址 org.springframework.boot.SpringApplicationRunListener=应用启动监听器
配置
// run方法 public ConfigurableApplicationContext run(String... args) { StopWatch stopWatch = new StopWatch(); stopWatch.start(); DefaultBootstrapContext bootstrapContext = createBootstrapContext(); ConfigurableApplicationContext context = null; configureHeadlessProperty(); SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting(bootstrapContext, this.mainApplicationClass); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments); configureIgnoreBeanInfo(environment); Banner printedBanner = printBanner(environment); context = createApplicationContext(); context.setApplicationStartup(this.applicationStartup); prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner); refreshContext(context); afterRefresh(context, applicationArguments); stopWatch.stop(); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch); } listeners.started(context); callRunners(context, applicationArguments); } catch (Throwable ex) { handleRunFailure(context, ex, listeners); throw new IllegalStateException(ex); } try { listeners.running(context); } catch (Throwable ex) { handleRunFailure(context, ex, null); throw new IllegalStateException(ex); } return context; }
如有错误还请各位大佬不吝赐教