当前位置:   article > 正文

springboot 两大特性和启动类分析_springboot两大特性

springboot两大特性

springboot中的两个基本jar包

spring-boot-starter-parent (非必需)

作为parent之后所有官方定义的starter包都适配自己的version,减少jar包冲突,

里边引用了dependencies,在dependencyManage中定义了所有版本,可以直接用

如果不以这个包作为parent,需要自己指定版本,或者自己定义dependencyManage

spring-boot-starter (必需)

 springboot 必须的jar包,核心启动器,包含自动配置,日志,spring-core,yaml

其中 spring-boot 包含了启动类 run 方法的相关包

spring-boot-autoconfigure 包含了  @SpringBootApplication 相关包(自动配置)

springboot 两大特性:

  • 起步依赖

起步依赖本质上是一个Maven项目对象模型(Project Object Model),即pom,定义了对其他库的传递依赖;

简单来说,起步依赖就是将具备某种功能的坐标打包到一起,并提供一些默认的功能。

  • 自动配置

springboot在我们引入对应的starter时,会自动的将一些配置类的bean注册进ioc容器,并且自动为我们配置一些组件的相关配置,

1、在需要的地方可以直接使用@Autowired或者@Resource等注解来使用

2、无需配置或者只需要少量配置就能运行编写的项目。

依赖管理

  • 版本依赖(为什么jar包不需要指定版本?

在springboot项目中,都会有一个spring-boot-starter-parent的依赖

  1. <!-- Spring Boot父项目依赖管理 -->
  2. <parent>
  3. <groupId>org.springframework.boot</groupId>
  4. <artifactId>spring-boot-starter-parent<11./artifactId>
  5. <version>2.2.2.RELEASE</version>
  6. <relativePath/> <!-- lookup parent from repository -->
  7. </parent>

在上述配置中,将spring-boot-starter-parent依赖作为Spring Boot项目的统一父项目依赖管理,并将项目版本号统一为2.2.2.RELEASE,该版本号根据实际开发需求是可以修改的。

点进spring-boot-starter-parent去查看,发现其引用了一个spring-boot-dependencies的parent

在spring-boot-dependencies里边有一个properties节点维护了所有的版本号(只是针对于spring官方维护的starter,第三方定义的不在其内),有一个dependencyManagement节点,配置了所有的jar包依赖,后期引用jar包时只需要写groupId和artifactId即可。

  1. <properties>
  2. <activemq.version>5.15.11</activemq.version>
  3. <antlr2.version>2.7.7</antlr2.version>
  4. <appengine-sdk.version>1.9.77</appengine-sdk.version>
  5. <artemis.version>2.10.1</artemis.version>
  6. <aspectj.version>1.9.5</aspectj.version>
  7. ...
  8. </properties>
  9. <dependencyManagement>
  10. <dependencies>
  11. <dependency>
  12. <groupId>org.springframework.boot</groupId>
  13. <artifactId>spring-boot</artifactId>
  14. <version>2.2.2.RELEASE</version>
  15. </dependency>
  16. <dependency>
  17. <groupId>org.springframework.boot</groupId>
  18. <artifactId>spring-boot-test</artifactId>
  19. <version>2.2.2.RELEASE</version>
  20. </dependency>
  21. <dependency>
  22. <groupId>org.springframework.boot</groupId>
  23. <artifactId>spring-boot-test-autoconfigure</artifactId>
  24. <version>2.2.2.RELEASE</version>
  25. </dependency>
  26. ...
  27. </dependencies>
  28. </dependencyManagement>

例如引入web相关依赖,只需要在dependencies节点下引入如下依赖即可:

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-web</artifactId>
  4. </dependency>
  • jar包依赖(为什么引入一个jar包就可以直接启动?

每一个starter中都封装了当前starter的所有依赖jar包,所以只需要引入对应的starter即可应用该组件,以spring-boot-starter-web为例:

  1. <dependencies>
  2. <dependency>
  3. <groupId>org.springframework.boot</groupId>
  4. <artifactId>spring-boot-starter</artifactId>
  5. <version>2.2.2.RELEASE</version>
  6. <scope>compile</scope>
  7. </dependency>
  8. <dependency>
  9. <groupId>org.springframework.boot</groupId>
  10. <artifactId>spring-boot-starter-json</artifactId>
  11. <version>2.2.2.RELEASE</version>
  12. <scope>compile</scope>
  13. </dependency>
  14. <dependency>
  15. <groupId>org.springframework.boot</groupId>
  16. <artifactId>spring-boot-starter-tomcat</artifactId>
  17. <version>2.2.2.RELEASE</version>
  18. <scope>compile</scope>
  19. </dependency>
  20. <dependency>
  21. <groupId>org.springframework.boot</groupId>
  22. <artifactId>spring-boot-starter-validation</artifactId>
  23. <version>2.2.2.RELEASE</version>
  24. <scope>compile</scope>
  25. <exclusions>
  26. <exclusion>
  27. <artifactId>tomcat-embed-el</artifactId>
  28. <groupId>org.apache.tomcat.embed</groupId>
  29. </exclusion>
  30. </exclusions>
  31. </dependency>
  32. <dependency>
  33. <groupId>org.springframework</groupId>
  34. <artifactId>spring-web</artifactId>
  35. <version>5.2.2.RELEASE</version>
  36. <scope>compile</scope>
  37. </dependency>
  38. <dependency>
  39. <groupId>org.springframework</groupId>
  40. <artifactId>spring-webmvc</artifactId>
  41. <version>5.2.2.RELEASE</version>
  42. <scope>compile</scope>
  43. </dependency>
  44. </dependencies>

在pom文件中dependencies节点下引入了web环境依赖的所有jar包,包括web-mvc和tomcat等,以及spring-boot-starter中自带的spring的ioc和aop相关jar包,这样在引入当前starter时就可以实现web场景开发。

springboot官方除了提供web依赖容器外,还提供了其他场景相关的starter,从官方文档中可以看到如下34种,根据版本不同,可能还会有对应的增减。

这些依赖启动器(starter)适用于不同的场景开发,使用时只要在pom.xml文件中导入对应的依赖启动器即可,不需要特别指定version;

需要说明的是,Spring Boot官方并不是针对所有场景开发的技术框架都提供了场景启动器,例如数据 库操作框架MyBatis、阿里巴巴的Druid数据源等,Spring Boot官方就没有提供对应的依赖启动器。为 了充分利用Spring Boot框架的优势,在Spring Boot官方没有整合这些技术框架的情况下,MyBatis、 Druid等技术框架所在的开发团队主动与Spring Boot框架进行了整合,实现了各自的依赖启动器,例如 mybatis-spring-boot-starter、druid-spring-boot-starter等。我们在pom.xml文件中引入这些第三方的依赖启动器时,切记要配置对应的版本号。

自动配置(启动类分析)

Spring Boot应用的启动入口是@SpringBootApplication注解标注类中的main()方法

  1. import org.springframework.boot.SpringApplication;
  2. import org.springframework.boot.autoconfigure.SpringBootApplication;
  3. @SpringBootApplication
  4. public class SpringbootApplication {
  5. public static void main(String[] args) {
  6. SpringApplication.run(SpringbootApplication.class, args);
  7. }
  8. }

代码包含两部分,一个是@SpringBootApplication注解,一个是run()方法。

@SpringBootApplication注解

点进去发现@SpringBootApplication注解是一个组合注解,上边是注解的元数据信息,下边引入了其他注解:

  • @Target                 注解的适用范围,Type表示注解可以描述在类、接口、注解或枚举中
  • @Retention           表示注解的生命周期,Runtime运行时
  • @Documented      表示注解可以记录在javadoc中
  • @Inherited            表示可以被子类继承该注解
  • @SpringBootConfiguration             标明该类为配置类
  • @EnableAutoConfiguration            启动自动配置功能
  • @ComponentScan                         包扫描器

@SpringBootConfiguration

@SpringBootConfiguration 注解表示该类是springboot的配置类,它是一个组合注解,引入了一个 @Configuration 的注解(Spring的注解,本身是 @Component 注解的一个封装,相当于起了个别名),标识是一个配置类

 

@EnableAutoConfiguration

@EnableAutoConfiguration 注解表示开启自动配置功能,它是一个组合注解,引入了一个 @AutoConfigurationPackage 注解(会把@springbootApplication注解标注的类所在包名拿到,并且对该包及其子包进行扫描,将组件添加到容器中) 和 一个 @Import 注解(帮助springboot应用将所有符合条件的@Configuration配置都加载到当前SpringBoot创建并使用的IoC容器(ApplicationContext)中)

 @AutoConfigurationPackage

 @AutoConfigurationPackage 注解,引入了一个 @Import 注解(spring框架的底层注解,它的作用就是给容器中导入某个组件类,此处就是将Registrar这个组件类导入到容器中)

 对应实现 org.springframework.beans.factory.support.DefaultListableBeanFactory

 跟代码发现,@Import(AutoConfigurationPackages.Registrar.class) 实际上是将  org.springframework.boot.autoconfigure.AutoConfigurationPackages  注册到 beanDefinitionMap 中

@Import(AutoConfigurationImportSelector.class)

将 AutoConfigurationImportSelector这个类导入到spring容器中, AutoConfigurationImportSelector可以帮助springboot应用将所有符合条件的@Configuration配置 都加载到当前SpringBoot创建并使用的IoC容器(ApplicationContext)中

点进去发现 AutoConfigurationImportSelector 类主要是实现了DeferredImportSelector 接口,其他 Aware 结尾的接口是获取一些配置信息,ordered 进行排序,当前功能

DeferredImportSelector 接口继承了 ImportSelector 接口,有一个默认的方法  getImportGroup() ,返回了内部接口 Group 的一个实现类,Group 有 process 和 selectImports 两个方法需要实现,核心逻辑都在这里边

在spring加载 ioc 容器时,AbstractApplicationContext 类里边的核心方法 refresh() 会调用  ConfigurationClassParser 类的 getImports() 方法,getImports 方法会执行所有实现了DeferredImportSelector 接口的实现

 在 AutoConfigurationImportSelector 的实现中,process 方法会去调用 getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) 方法,读取所有符合条件的配置并返回 AutoConfigurationEntry ,并调用 selectImports 方法将 AutoConfigurationEntry  转换成 Iterable<Entry>,最终通过 loadBeanDefinitionsForConfigurationClass 方法注册到 BeanDefinationMap中

 上边提到的DeferredImportSelector 接口继承的 ImportSelector 接口,需要实现  String[] selectImports(AnnotationMetadata var1)  方法;

在AutoConfigurationImportSelector的实现中,selectImports 方法会去调用getAutoConfigurationEntry 方法,返回所有的配置类(断点调试未走到该方法)

默认情况下,有一个 CacheConfigurationImportSelector ,实现了该方法,返回对应的配置类,此处cache默认返回了十个springboot定义的缓存配置类

后边通过  processImports 方法将配置类注册到 BeanDefinationMap中

 跟代码发现,@Import(AutoConfigurationImportSelector.class) 实际上是将  resources/META-INF/spring.factories 文件中读取到的 EnableAutoConfiguration 根据条件筛选后的配置以及实现 ImportSelector 接口中  String[] selectImports(AnnotationMetadata var1)  方法获取到的配置类注册到 beanDefinitionMap 中

 Run方法

分为 new SpringApplication(primarySources)run(args) 两步,整个执行流程如图所示:

  • new 方法

指定主类判断servlet容器类型,后边run方法会根据类型创建对应容器,设置了bootstrapRegistryInitializersinitializerslisteners,并确定了启动类

  1. /**
  2. * Create a new {@link SpringApplication} instance. The application context will load
  3. * beans from the specified primary sources (see {@link SpringApplication class-level}
  4. * documentation for details). The instance can be customized before calling
  5. * {@link #run(String...)}.
  6. * @param resourceLoader the resource loader to use
  7. * @param primarySources the primary bean sources
  8. * @see #run(Class, String[])
  9. * @see #setSources(Set)
  10. */
  11. @SuppressWarnings({ "unchecked", "rawtypes" })
  12. public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
  13. this.resourceLoader = resourceLoader;
  14. Assert.notNull(primarySources, "PrimarySources must not be null");
  15. this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
  16. // 根据加载的类路径是否存在对应类型来判断 web 容器的类型:servlet,reactive,none,没设置容器默认为 none
  17. this.webApplicationType = WebApplicationType.deduceFromClasspath();
  18. // 从 resources/META-INF/spring.factories 文件中读取配置的BootstrapRegistryInitializer并实例化
  19. this.bootstrapRegistryInitializers = new ArrayList<>(
  20. getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
  21. // 从 resources/META-INF/spring.factories 文件中读取配置的ApplicationContextInitializer并实例化
  22. setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
  23. // 从 resources/META-INF/spring.factories 文件中读取配置的ApplicationListener并实例化
  24. setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
  25. // 从堆栈跟踪对象数组中,找到main方法对应的类作为启动类
  26. this.mainApplicationClass = deduceMainApplicationClass();
  27. }
  • run 方法 

加载配置,启动监听器,打印banner,根据servlet类型创建了ioc容器并加载实例刷新容器,执行 callRunners 方法(springboot如何在启动结束后执行方法?

  1. /**
  2. * Run the Spring application, creating and refreshing a new
  3. * {@link ApplicationContext}.
  4. * @param args the application arguments (usually passed from a Java main method)
  5. * @return a running {@link ApplicationContext}
  6. */
  7. public ConfigurableApplicationContext run(String... args) {
  8. long startTime = System.nanoTime();
  9. // 根据从spring.factory 中读取到bootstrapRegistryInitializers初始化启动上下文容器
  10. DefaultBootstrapContext bootstrapContext = createBootstrapContext();
  11. ConfigurableApplicationContext context = null;
  12. configureHeadlessProperty();
  13. // 获取并启动监听器,从 resources/META-INF/spring.factories 文件中读取配置的 SpringApplicationRunListener
  14. SpringApplicationRunListeners listeners = getRunListeners(args);
  15. // 启动监听器,发布事件,invokeListener,onApplicationEvent
  16. listeners.starting(bootstrapContext, this.mainApplicationClass);
  17. try {
  18. ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
  19. // 根据监听器以及启动参数来准备环境
  20. ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
  21. // 排除不需要的运行环境
  22. configureIgnoreBeanInfo(environment);
  23. // 根据bannerMode判断是否打印,读取resource目录下的banner文件并打印
  24. Banner printedBanner = printBanner(environment);
  25. // 根据 new SpringApplication 时判断的servlet(webApplicationType)类型,创建IOC容器
  26. context = createApplicationContext();
  27. // 设置 DefaultApplicationStartup
  28. context.setApplicationStartup(this.applicationStartup);
  29. // spring ioc 容器前置处理,将environment,listeners,applicationArguments,printedBanner 加载进容器
  30. // 将springApplicationArguments,springBootBanner加载进单例池,设置是否允许循环依赖,是否允许覆盖注册,是否允许懒加载
  31. // 将来源文件(启动类)注册进 AnnotatedBeanDefinitionReader ,后边根据对应的来源文件去进行加载
  32. prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
  33. // 注册关闭勾子,在jvm关闭时关闭上下文;调用 spring 的refresh方法,刷新ioc容器,
  34. // 这一步执行完之后,beanDefinationMap 和 singletonObject 中就会加载进去所有扫描到的类
  35. refreshContext(context);
  36. // 后置处理,空模板,可重写扩展
  37. afterRefresh(context, applicationArguments);
  38. Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
  39. if (this.logStartupInfo) {
  40. new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
  41. }
  42. // 观察者模式,设置回调事件,发布 SpringApplicationRunListeners 的 started 监听事件
  43. listeners.started(context, timeTakenToStartup);
  44. // 启动完成之后执行 ApplicationRunner 和 CommandLineRunner,都是执行run方法,没指定order的情况下,限制性ApplicationRunner,再执行CommandLineRunner
  45. // ApplicationRunner 参数是 kv 格式 ApplicationArguments args
  46. // CommandLineRunner 参数是数组格式 String... args
  47. callRunners(context, applicationArguments);
  48. }
  49. catch (Throwable ex) {
  50. handleRunFailure(context, ex, listeners);
  51. throw new IllegalStateException(ex);
  52. }
  53. try {
  54. Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
  55. listeners.ready(context, timeTakenToReady);
  56. }
  57. catch (Throwable ex) {
  58. handleRunFailure(context, ex, null);
  59. throw new IllegalStateException(ex);
  60. }
  61. // 返回创建完的容器
  62. return context;
  63. }

run 方法中最核心的两步  prepareContext 和 refreshContext

prepareContext 方法

做了spring ioc 容器前置处理,

将environment,listeners,applicationArguments,printedBanner 加载进容器, 

将springApplicationArguments,springBootBanner加载进单例池,设置是否允许循环依赖,是否允许覆盖注册,是否允许懒加载 ,

将来源文件(启动类)注册进 AnnotatedBeanDefinitionReader ,后边根据对应的来源文件去进行加载

 refreshContext 方法

注册关闭勾子,在jvm关闭时关闭上下文;

调用 spring 的refresh方法,刷新ioc容器,AbstractApplicationContext 类的 refresh() 方法,是spring 容器的核心方法,ioc,aop 都会在这一步处理,需要单独去分析

  1. private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
  2. ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
  3. ApplicationArguments applicationArguments, Banner printedBanner) {
  4. // 将前置加载的配置信息放到 context 中
  5. context.setEnvironment(environment);
  6. // 对 context 进行后置处理,配置beanName生成器,配置加载器、类加载器,转换服务
  7. postProcessApplicationContext(context);
  8. // 在 context 容器中,对前边加载的 initializers 进行初始化
  9. applyInitializers(context);
  10. // 观察者模式,设置回调事件,发布 SpringApplicationRunListeners 的 context-prepared 监听事件
  11. listeners.contextPrepared(context);
  12. // 通过 ApplicationEventMulticaster 发布启动上下文关闭事件 BootstrapContextClosedEvent
  13. bootstrapContext.close(context);
  14. if (this.logStartupInfo) {
  15. logStartupInfo(context.getParent() == null);
  16. logStartupProfileInfo(context);
  17. }
  18. ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
  19. // 注册 springApplicationArguments 进单例池 singletonObjects
  20. beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
  21. if (printedBanner != null) {
  22. // 注册 springBootBanner 进单例池 singletonObjects
  23. beanFactory.registerSingleton("springBootBanner", printedBanner);
  24. }
  25. if (beanFactory instanceof AbstractAutowireCapableBeanFactory) {
  26. // 设置是否允许循环引用
  27. ((AbstractAutowireCapableBeanFactory) beanFactory).setAllowCircularReferences(this.allowCircularReferences);
  28. if (beanFactory instanceof DefaultListableBeanFactory) {
  29. // 设置是否允许 bean 覆盖注入
  30. ((DefaultListableBeanFactory) beanFactory)
  31. .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
  32. }
  33. }
  34. if (this.lazyInitialization) {
  35. // 如果是懒加载,设置对应的后置处理器
  36. context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
  37. }
  38. // DefaultPropertiesPropertySource 设置为最低优先级,最后执行
  39. context.addBeanFactoryPostProcessor(new PropertySourceOrderingBeanFactoryPostProcessor(context));
  40. // 获取所有的来源文件,主要是启动类
  41. Set<Object> sources = getAllSources();
  42. Assert.notEmpty(sources, "Sources must not be empty");
  43. // 将对应的源注册进 AnnotatedBeanDefinitionReader,后边根据对应的来源文件去进行加载
  44. load(context, sources.toArray(new Object[0]));
  45. // 观察者模式,设置回调事件,发布 SpringApplicationRunListeners 的 context-loaded 监听事件
  46. listeners.contextLoaded(context);
  47. }

prepareContext  中的 postProcessApplicationContext(context)  方法

  1. /**
  2. * Apply any relevant post processing the {@link ApplicationContext}. Subclasses can
  3. * apply additional processing as required.
  4. * @param context the application context
  5. */
  6. protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
  7. if (this.beanNameGenerator != null) {
  8. // 如果当前容器名字的生成器不为空,往单例池中注入配置beanName生成器 internalConfigurationBeanNameGenerator
  9. context.getBeanFactory().registerSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,
  10. this.beanNameGenerator);
  11. }
  12. if (this.resourceLoader != null) {
  13. // 如果配置的加载器不为空,将配置加载器、类加载器都保存到容器工厂中
  14. if (context instanceof GenericApplicationContext) {
  15. ((GenericApplicationContext) context).setResourceLoader(this.resourceLoader);
  16. }
  17. if (context instanceof DefaultResourceLoader) {
  18. ((DefaultResourceLoader) context).setClassLoader(this.resourceLoader.getClassLoader());
  19. }
  20. }
  21. if (this.addConversionService) {
  22. // 将环境中配置的的转换服务保存到容器中
  23. context.getBeanFactory().setConversionService(context.getEnvironment().getConversionService());
  24. }
  25. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/花生_TL007/article/detail/479563
推荐阅读
相关标签
  

闽ICP备14008679号