SpringBoot启动流程与自动装配
是什么
Spring Boot基于Spring框架之上的一个微服务架构开发框架
大大简化了Spring的开发。因为Spring Boot提供了大量的自动配置。而且它是基于Java配置方式的开发(全注解)
Spring Boot与集成了许多第三方框架并进行了最小化自动配置
Spring Boot和Spring Cloud关系:Spring Cloud的开发需要用Spring Boot,反之不一定
官方介绍
Spring Boot使创建独立的、生产级的基于Spring的应用程序变得很容易,您可以“直接运行”这些应用程序。
Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that you can "just run".
我们对Spring平台和第三方库有自己的看法,这样您就可以轻松入门了。大多数Spring启动应用程序只需要很少的Spring配置。
We take an opinionated view of the Spring platform and third-party libraries so you can get started with minimum fuss. Most Spring Boot applications need very little Spring configuration.
特点
创建独立的Spring应用程序
Create stand-alone Spring applications
直接嵌入Tomcat、Jetty或Undertow(不需要部署WAR文件)
Embed Tomcat, Jetty or Undertow directly (no need to deploy WAR files)
提供独到的起始依赖去简化配置
Provide opinionated 'starter' dependencies to simplify your build configuration
在可能的情况下自动配置Spring和第三方库
Automatically configure Spring and 3rd party libraries whenever possible
提供可用于生产的特性,如度量标准、健康状况检查和外部化配置
Provide production-ready features such as metrics, health checks and externalized configuration
完全不需要代码生成,也不需要XML配置 去XML化
Absolutely no code generation and no requirement for XML configuration
启动流程
- 创建 SpringApplication
- 保存一些信息。
- 判定当前应用的类型。ClassUtils。Servlet
- bootstrappers:初始启动引导器(List):去spring.factories文件中找 org.springframework.boot.Bootstrapper
- 找 ApplicationContextInitializer 应用初始化器,去spring.factories找 ApplicationContextInitializer
- List<ApplicationContextInitializer<?>> initializers
- 找 ApplicationListener ,应用监听器。去spring.factories找 ApplicationListener
- List<ApplicationListener<?>> listeners
- 运行 SpringApplication
- StopWatch
- 记录应用的启动时间
- 创建引导上下文(Context环境)createBootstrapContext()
- 获取到所有之前的 bootstrappers 挨个执行 intitialize() 来完成对引导启动器上下文环境设置
- 让当前应用进入headless模式。java.awt.headless
- 获取所有 RunListener(运行监听器)【为了方便所有Listener进行事件感知】
- getSpringFactoriesInstances 去spring.factories找 SpringApplicationRunListener.
- 遍历 SpringApplicationRunListener 调用 starting 方法;
- 相当于通知所有感兴趣系统正在启动过程的人,项目正在 starting。
- 保存命令行参数;ApplicationArguments
- 准备环境 prepareEnvironment();
- 返回或者创建基础环境信息对象。StandardServletEnvironment
- 配置环境信息对象。
- 读取所有的配置源的配置属性值。
- 绑定环境信息
- 监听器调用 listener.environmentPrepared();通知所有的监听器当前环境准备完成
- 创建IOC容器(createApplicationContext())
- 根据项目类型(Servlet)创建容器,
- 当前会创建 AnnotationConfigServletWebServerApplicationContext
- 准备ApplicationContext IOC容器的基本信息 prepareContext()
- 保存环境信息
- IOC容器的后置处理流程。
- 应用初始化器;applyInitializers;
- 遍历所有的 ApplicationContextInitializer 。调用 initialize.。来对ioc容器进行初始化扩展功能
- 遍历所有的 listener 调用 contextPrepared。EventPublishRunListenr;通知所有的监听器contextPrepared
- 所有的监听器 调用 contextLoaded。通知所有的监听器 contextLoaded;
- 刷新IOC容器。refreshContext
- 创建容器中的所有组件(Spring注解)
- 容器刷新完成后工作?afterRefresh
- 所有监听 器 调用 listeners.started(context); 通知所有的监听器 started
- 调用所有runners;callRunners()
- 获取容器中的 ApplicationRunner
- 获取容器中的 CommandLineRunner
- 合并所有runner并且按照@Order进行排序
- 遍历所有的runner。调用 run 方法
- 如果以上有异常,
- 调用Listener 的 failed
- 调用所有监听器的 running 方法 listeners.running(context); 通知所有的监听器 running
- running如果有问题。继续通知 failed 。调用所有 Listener 的 failed;通知所有的监听器 failed
启动类
1 @SpringBootApplication 2 @EnableAutoConfiguration(exclude = HibernateJpaAutoConfiguration.class) 3 @EnableScheduling 4 public class Application { 5 6 public static void main(String[] args) { 7 SpringApplication.run(Application.class, args); 8 }
SpringApplication.run方法
new sprngApplication()创建
public SpringApplication(Class<?>... primarySources) { this(null, primarySources); } public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { this.resourceLoader = resourceLoader; //断言启动类不为空 Assert.notNull(primarySources, "PrimarySources must not be null"); this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); //应用类型判断,servlet还是react this.webApplicationType = WebApplicationType.deduceFromClasspath(); //设置初始化器 setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); //设置监听器 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); this.mainApplicationClass = deduceMainApplicationClass(); }
run运行
/** * Run the Spring application, creating and refreshing a new * {@link ApplicationContext}. * @param args the application arguments (usually passed from a Java main method) * @return a running {@link ApplicationContext} */ public ConfigurableApplicationContext run(String... args) { //启停监听器 StopWatch stopWatch = new StopWatch(); //记录启动时间 stopWatch.start(); ConfigurableApplicationContext context = null; Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>(); //当前应用进入headless自力更生模式 configureHeadlessProperty(); //获取所有运行时监听器 通过SpringFactoryInstance去spring.factories下找SpringApplicationRunListener SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting(); try { //保存传过来的args参数 ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); //准备环境信息 包括加载外部配置源 调用listener.envirementPrepared()通知监听器环境准备完 ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); //配置需要忽略的信息 configureIgnoreBeanInfo(environment); Banner printedBanner = printBanner(environment); //创建IOC容器 根据当前项目类型 servlet or reactive 这里是servlet context = createApplicationContext(); exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context); //准备IOC容器信息 prepareContext(context, environment, listeners, applicationArguments, printedBanner); //刷新IOC容器 创建容器中的所有组件 refreshContext(context); //刷新后的处理 暂时没干任何工作 afterRefresh(context, applicationArguments); //监听启动完成花费多少时间 stopWatch.stop(); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch); } //通知监听器项目已经启动 listeners.started(context); //调用所有能遍历到的runner,获取ApplicationRunner和CommandLineRuner 执行它们的run方法 callRunners(context, applicationArguments); } //如果有异常,调用listeners.failed 容器创建失败 catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, listeners); throw new IllegalStateException(ex); } try { //调用listener的running方法,通知监听器程序已经进入runnig状态 listeners.running(context); } catch (Throwable ex) { //如果有异常,调用listeners.failed 容器创建失败 handleRunFailure(context, ex, exceptionReporters, null); throw new IllegalStateException(ex); } //返回整个IOC容器 return context; }
准备IOC容器信息
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) { //保存基础环境 context.setEnvironment(environment); //IOC容器的后置处理 postProcessApplicationContext(context); //应用之前的那些初始化器,对IOC容器记性初始化扩展 applyInitializers(context); //通知监听器上下文准备完成 listeners.contextPrepared(context); if (this.logStartupInfo) { logStartupInfo(context.getParent() == null); logStartupProfileInfo(context); } // Add boot specific singleton beans ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); beanFactory.registerSingleton("springApplicationArguments", applicationArguments); if (printedBanner != null) { beanFactory.registerSingleton("springBootBanner", printedBanner); } if (beanFactory instanceof DefaultListableBeanFactory) { ((DefaultListableBeanFactory) beanFactory) .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding); } if (this.lazyInitialization) { context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor()); } // Load the sources Set<Object> sources = getAllSources(); Assert.notEmpty(sources, "Sources must not be empty"); load(context, sources.toArray(new Object[0])); listeners.contextLoaded(context); }
刷新IOC容器 创建容器中的所有组件
private void refreshContext(ConfigurableApplicationContext context) { //spring核心源码 刷新底层的ApplicationContext。 refresh((ApplicationContext) context); if (this.registerShutdownHook) { try { //向JVM运行时注册一个关闭钩子,在JVM关闭时关闭这个上下文 context.registerShutdownHook(); } catch (AccessControlException ex) { // Not allowed in some environments. } } } /** * Refresh the underlying {@link ApplicationContext}. * @param applicationContext the application context to refresh */ protected void refresh(ConfigurableApplicationContext applicationContext) { applicationContext.refresh(); } /** *spring经典的整个初始化过程 */ @Override public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. prepareRefresh(); // Tell the subclass to refresh the internal bean factory. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. prepareBeanFactory(beanFactory); try { // Allows post-processing of the bean factory in context subclasses. postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context. invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. registerBeanPostProcessors(beanFactory); // Initialize message source for this context. initMessageSource(); // Initialize event multicaster for this context. initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. onRefresh(); // Check for listener beans and register them. registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. //实例化所有剩余的非延迟加载的单例组件 finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. finishRefresh(); } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); } // Destroy already created singletons to avoid dangling resources. destroyBeans(); // Reset 'active' flag. cancelRefresh(ex); // Propagate exception to caller. throw ex; } finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); } } }
自动装配原理
引入
@springbootApplication注解中自动配置相关
@SpringBootConfiguration @EnableAutoConfiguration @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) public @interface SpringBootApplication{} ======================
@EnableAutoConfiguration中有@AutoConfigurationPackage和@Import({AutoConfigurationImportSelector.class})
@AutoConfigurationPackage进行组件注册,@Import(AutoConfigurationImportSelector.class)导入的AutoConfigurationImportSelector组件调用loadSpringFactories()方法,利用工厂加载,扫描当前系统里面所有META-INF/spring.factories位置的文件,得到所有自动配置组件,
但是会按需配置,这个是通过@Condition注解族实现的,比如@ConditionalOnMissingBean指定以用户自己配置的优先
@AutoConfigurationPackage
@Import(AutoConfigurationPackages.Registrar.class) //给容器中导入一个组件 public @interface AutoConfigurationPackage {} //利用Registrar给容器中导入一系列组件 //将指定的一个包下的所有组件导入进来?MainApplication 所在包下。
@Import(AutoConfigurationImportSelector.class)
1、利用getAutoConfigurationEntry(annotationMetadata);给容器中批量导入一些组件 2、调用List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes)获取到所有需要导入到容器中的配置类 3、利用工厂加载 Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader);得到所有的组件 4、从META-INF/spring.factories位置来加载一个文件。 默认扫描我们当前系统里面所有META-INF/spring.factories位置的文件 spring-boot-autoconfigure-2.3.4.RELEASE.jar包里面也有META-INF/spring.factories
文件里面写死了spring-boot一启动就要给容器中加载的所有配置类 spring-boot-autoconfigure-2.3.4.RELEASE.jar/META-INF/spring.factories # Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\ org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\ org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\ org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\ org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\ org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfigurati
按需开启自动配置
可以修改默认配置
@Bean @ConditionalOnBean(MultipartResolver.class) //容器中有这个类型组件 @ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME) //容器中没有这个名字 multipartResolver 的组件 public MultipartResolver multipartResolver(MultipartResolver resolver) { //给@Bean标注的方法传入了对象参数,这个参数的值就会从容器中找。 //SpringMVC multipartResolver。防止有些用户配置的文件上传解析器不符合规范 // Detect if the user has created a MultipartResolver but named it incorrectly return resolver; }
总结
- SpringBoot先加载所有的自动配置类 xxxxxAutoConfiguration
- 每个自动配置类按照条件进行生效,默认都会绑定配置文件指定的值。xxxxProperties里面拿。xxxProperties和配置文件进行了绑定
- 生效的配置类就会给容器中装配很多组件
- 只要容器中有这些组件,相当于这些功能就有了
- 定制化配置
- 用户直接自己@Bean替换底层的组件
- 用户去看这个组件是获取的配置文件什么值就去修改。
作者: deity-night
出处: https://www.cnblogs.com/deity-night/
关于作者:码农
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出, 原文链接 如有问题, 可邮件(***@163.com)咨询.