导航
前言
我知道很多小伙伴可能都是从Spring转到SpringBoot的,明白之前整合Spring + MyBatis + SpringMVC我们需要写一大堆的配置文件,堪称配置文件地狱,而且随着项目的越来越庞大,配置文件也会越来越繁琐,这在一定程度上也给开发者带来了困扰,于是 SpringBoot 就应运而生了。
自从使用SpringBoot后,新建一个项目几乎不需要做任何改动,我们就可以运行起来。pom文件里,我们只需要引入一个spring-boot-starter-web就可以,之前我们所做的一切,SpringBoot都在底层帮我们做了。
本篇就来学习一下SpringBoot是如何做依赖管理以及自动配置的。
依赖管理
大家都知道SpringBoot项目,pom.xml文件都会给我们定义一个parent节点

该节点指定了version版本号,所以在pom.xml文件里我们很多引入的jar都没有定义版本号,但这样也不会出错,因为SpringBoot帮我们为一些常用的jar包指定了版本号。
ctrl + 鼠标左键点击进入spring-boot-starter-parent这个jar包,会发现它的父项目是spring-boot-dependencies

而在这个jar包里,就会发现声明了很多开发中常用jar的版本号

看到这里也解惑了上一期 SpringBoot系列(二)入门,快速构建项目中我没有指定版本也不会报错的问题。
所以在pom.xml文件中引入jar的时候,如果该jar在spring-boot-dependencies中定义了版本号,那么你可以不写。如果你想使用其他的版本号,那么也可以在pom.xml中定义version,遵循就近原则。比如我现在想自己从新定义 kafka 的版本号,我就可以添加依赖时,也添加上版本号。
<dependency>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka-clients</artifactId>
<version>2.7.1</version>
</dependency>
Web项目启动详解
我们日常使用SpringBoot开发web项目时,简单的配置就是只导入了 spring-boot-starter-web 这 一个依赖就可以写接口并且进行访问,因为在这个starter中整合了我们之前写Spring项目时引入的spring-aop、spring-context、spring-webmvc等jar包,包括tomcat,所以SpringBoot项目不需要外部的tomcat,只需要启动application类使用内置的tomcat服务器即可。

在SpringBoot项目中,根据官方文档,有各种场景的spring-boot-starter-*可以使用,只要引入了starter,这个场景所有常规需要的依赖就会自动引入。
所以我们在启动项目时启动器最底层的依赖就是spring-boot-starter,该jar包是核心启动包,包含了自动配置的支持,日志以及YAML。Core starter, including auto-configuration support, logging and YAML,来自官方描述。
在上图中我们看到有 spring-boot-autoconfigure 依赖,这个就关系到SpringBoot自动配置功能。
自动装配
一、SpringApplication.run()
首先我们要确认项目的启动入口就是通过 SpringApplication.run()来启动的,所以我们就先从这里入手一步步来解析是如何自动装载的。

我们进入到SpringApplication这个类中看一下run()方法的核心实现,都注释了看解释,要重点关注红框两行代码

SpringApplication.run()方法中,我把关键点用序号标识出来了。
- 第一个就是创建ApplicationContext容器。
- 第二个是刷新ApplicationContext容器。
在创建ApplicationContext时,会根据用户是否明确设置了ApplicationContextClass类型以及初始化阶段的推断结果,决定为当前SpringBoot应用创建什么类型的ApplicationContext。

下面开始初始化各种插件在异常失败后给出的提示。
然后执行准备刷新上下文的一些操作。其实prepareContext()方法也是非常关键的,它起到了一个承上启下的作用。下面我们来看一下prepareContext()方法里面具体执行了什么。
关键的地方我也标注出来了,主要就是getAllSoures()方法,这个方法中,获取到的一个source就是启动类LearnApplication。

这样就通过获取这个启动类就可以在后load()方法中去加载这个启动类到容器中。
然后,后面再通过listeners.contextLoaded(context);
将所有监听器加载到ApplicationContext容器中。
最后就是我们上面说的核心的第二部刷新ApplicationContext容器操作,如果没有这一步操作上面的内容也都白做的,通过SpringApplication的refreshContext(context)方法完成最后一道工序将启动类上的注解配置,刷新到当前运行的容器环境中。
二、启动类的注解
在启动类上有一个 @SpringBootApplication 注解,这一个注解帮我们启动了SpringBoot项目,其背后默认帮我们配置了很多自动配置类。我们点进去看一下 @SpringBootApplication 这个注解。
@SpringBootApplication

解说一下三个注解:
- @SpringBootConfiguration : Spring Boot的配置类,标注在某个类上,表示这是一个Spring Boot的配置类(等于xml方式下.xml文件)。
- @EnableAutoConfiguration: 开启自动配置类,SpringBoot的精华所在。
- @ComponentScan:包扫描,等同于xml下开启并设置包扫描路径。
@EnableAutoConfiguration
@EnableAutoConfiguration:告诉SpringBoot开启自动配置功能,这样自动配置才能生效。

-
@AutoConfigurationPackage:自动配置包
-
@Import: 导入自动配置的组件
-
- @Import:通常用于有时没有把某个类注入到IOC容器中,但在运用的时候需要获取该类对应的bean,此时就需要用到
@Import注解。加入IOC容器的方式有很多种,当然@Bean注解也可以,但是@Import注解快速导入的方式更加便捷。
- @Import:通常用于有时没有把某个类注入到IOC容器中,但在运用的时候需要获取该类对应的bean,此时就需要用到
@AutoConfigurationPackage

它其实是注册了一个Bean的定义,返回了当前主程序类的同级以及子级的包组件。如果你把类写在了APP类的上级时SpringBoot便不会帮你加载,报出如404的问题,这也就是为什么,我们要把App放在项目的最高级中。
-
SpringApplication.run(App.class, args);为什么要传入主启动类?
-
- 其一方面就是要根据该类去解析他所在包,并实现对同级和下级类的扫描。
@Import

到这就到自动装配的核心了,离成功不远了,咱们直接看重点,通过继承 **ImportSelector**重写selectImports方法。
该方法奠定了SpringBoot自动批量装配的核心功能逻辑。

可以看到getCandidateConfigurations()方法,它其实是去加载
FACTORIES_RESOURCE_LOCATION=“META-INF/spring.factories” 外部文件。这个外部文件里面,默认有很多自动配置的类,这些类的定义信息将会被SpringBoot批量的加载到Bean定义Map中等待被创建实例。如下:

其实通过查阅资料发现,这就是一种自定义SPI的实现方式的功能。
那么我们以第一个配置类:
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration来看一下,这些类都是如果实现的。
打开org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration的源码:

我们看到这个类有三个注解@Configuration、@AutoConfigureAfter、@ConditionalOnProperty、因为有@Configuration注解所以它也是一个配置类,然后第二注解中的参数类JmxAutoConfiguration.class进入之后是这样的:

也是存在@ConditionalOnProperty注解的。那看来关键点就是@ConditionalOnProperty这个注解了。
这个注解其实是一个条件判断注解,这个条件注解后面的参数的意思是当存在系统属性前缀为spring.application.admin,并且属性名称为enabled,并且值为true时,才加载当前这个Bean并进行实例化。
这种spring4.0后面出现的的条件注解,可以极大的增加了框架的灵活性和扩展性,可以保证很多组件可以通过后期配置,而且阅读源码的人,通过这些注解就能明白在什么情况下才会实例化当前Bean。
后面还有不少这种条件注解呢:
- @ConditionalOnBean:当容器里有指定Bean的条件下
- @ConditionalOnClass:当类路径下有指定的类的条件下
- @ConditionalOnExpression:基于SpEL表达式为true的时候作为判断条件才去实例化
- @ConditionalOnJava:基于JVM版本作为判断条件
- @ConditionalOnJndi:在JNDI存在的条件下查找指定的位置
- @ConditionalOnMissingBean:当容器里没有指定Bean的情况下
- @ConditionalOnMissingClass:当容器里没有指定类的情况下
- @ConditionalOnWebApplication:当前项目时Web项目的条件下
- @ConditionalOnNotWebApplication:当前项目不是Web项目的条件下
- @ConditionalOnProperty:指定的属性是否有指定的值
- @ConditionalOnResource:类路径是否有指定的值
- @ConditionalOnOnSingleCandidate:当指定Bean在容器中只有一个,或者有多个但是指定首选的Bean
这些注解其实都是通过@Conditional注解扩展而来的,只是使用了不同的组合条件来判断是否需要加载和初始化当前Bean。
总结
springboot启动时,是依靠启动类的main方法来进行启动的,而main方法中执行的是SpringApplication.run()方法,而SpringApplication.run()方法中会创建spring的容器,并且刷新容器。而在刷新容器的时候就会去解析启动类,然后就会去解析启动类上的@SpringBootApplication注解,而这个注解是个复合注解,这个注解中有一个@EnableAutoConfiguration注解,这个注解就是开启自动配置,当我们使用@EnableAutoConfiguration注解激活自动装配时,实质对应着很多XXXAutoConfiguration类在执行装配工作,这些XXXAutoConfiguration类是在spring-boot-autoconfigure jar中的META-INF/spring.factories文件中配置好的,@EnableAutoConfiguration通过SpringFactoriesLoader机制创建XXXAutoConfiguration这些bean。XXXAutoConfiguration的bean是配置了符合这些条件注解的配置来进行装载的。
以上就是今天分享的,关于SpringBoot自动装配原理。
参考资料
SpringBoot配置项:https://docs.spring.io/spring-boot/docs/2.1.0.RELEASE/reference/htmlsingle/#common-application-properties
本文介绍了Spring Boot的依赖管理和自动装配原理。在依赖管理方面,Spring Boot为常用jar包指定版本号,开发者可按需调整。Web项目启动时,引入starter可自动引入所需依赖。自动装配通过SpringApplication.run()创建并刷新容器,启动类注解开启自动配置功能,依据条件注解加载Bean。
&spm=1001.2101.3001.5002&articleId=119537214&d=1&t=3&u=8e0d27a52a714a3a8b88046625642e42)
1079

被折叠的 条评论
为什么被折叠?



