1、引导加载自动配置类
MainApplication:启动类
@SpringBootApplication
public class MainApplication
@SpringBootApplication:是一个合成组件
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication
1.1、@SpringBootConfiguration
它由@Configuration标注,也就是说,它本质上是一个配置类,而与其他类不同的是,@SpringBootConfiguration表示当前类是核心配置类
1.2、@ComponentScan
指定要扫描哪些包,它有一些默认属性
1.3、@EnableAutoConfiguration(最重要)
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration
1.@AutoConfigurationPackage
自动配置包
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {}
// 给容器中导入组件
// 将指定包下的所有组件导入进去
导入的组件是一个Registrar
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
// 批量注册
// 通过注解元信息metadata获取到包名PackageImports.getPackageNames(),把包名封装到一个数组中toArray(new String[0]),然后注册register()进去
register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
}
@Override
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new PackageImports(metadata));
}
}
2.@Import(AutoConfigurationImportSelector.class)
利用getAutoConfigurationEntry(annotationMetadata)方法给容器中批量导入组件
// 给容器中批量导入组件
getAutoConfigurationEntry(annotationMetadata);
// 调用getCandidateConfigurations()获取所有需要导入到容器的配置类,返回一个list
getCandidateConfigurations(annotationMetadata, attributes);
// 利用工厂加载SpringFactoriesLoader.loadFactoryNames()获取所有组件,返回一个map
SpringFactoriesLoader.loadFactoryNames();
// 从META-INF/spring.facotries位置,加载得到所有组件,默认扫描我们当前系统里所有从META-INF/spring.facotries位置的文件,以map类型返回
loadSpringFactories();
// spring-boot-autoconfigure-2.5.3.jar包里也有META-INF/spring.facotries
// 文件里写死了SpringBoot已启动就会加载的所有配置类
加载的自动配置类有134个
这些自动配置类都写死在spring-boot-autoconfigure-2.5.3.jar包中的META-INF/spring.facotries
# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer
# Environment Post Processors
org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.boot.autoconfigure.integration.IntegrationPropertiesEnvironmentPostProcessor
# Auto Configuration Import Listeners
org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\
org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener
# Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnBeanCondition,\
org.springframework.boot.autoconfigure.condition.OnClassCondition,\
org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition
...
2、按需开启自动配置项
虽然,一股脑加载了所有配置类,但得益于SpringBoot的条件装配规则,最终会按需配置
3、修改默认配置
SpringBoot默认会在底层配好所有的组件。但是如果用户自己配置了以用户的优先
4、自动装配流程
xxxAutoConfiguration—>组件—>xxxProperties里面去拿值---->application.properties
总结:
- SpringBoot先加载所有自动配置类,xxxAutoConfiguration
- 每个自动配置类按照条件进行生效,默认都会绑定配置文件指定的值,xxxProperties里面拿。xxxProperties和配置文件进行了绑定
- 生效的配置类就会给容器中装配很多组件
- 只要容器中有这些组件,相当于这些功能就有了
- 定制化配置
- 用户直接用@Bean替换底层的组件
- 用户求改配置文件的值
5、starter的工作原理
问题
如果我们想引用一个组件,步骤是:导入jar包、配置bean、使用@Autowired注解,但是一旦jar包发生版本更新,可能会导致配置的bean也要发生改变,这就会降低我们使用这个jar包的意愿
SpringBoot的解决方案
利用starter,我们省去配置bean的步骤,而是直接配置到starter里面,就可以直接用@Autowired进行使用
starter的底层原理
SpringBoot启动包:spring-boot-starter
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
为什么可以不写版本号?
pom.xml
继承自spring-boot-starter-parent
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
spring-boot-starter-parent
继承自spring-boot-dependencies
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.5.4</version>
</parent>
spring-boot-dependencies
<properties>
<activemq.version>5.16.3</activemq.version>
...
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-amqp</artifactId>
<version>${activemq.version}</version>
</dependency>
...
</dependencies>
</dependencyManagement>
在spring-boot-dependencies里面,定义了核心依赖包的版本号
那为什么不直接把spring-boot-dependencies作为parent放到工程里去呢?
因为有时我们需要自己配置一些组件,而不是用SpringBoot默认配好的组件,这时需要加载资源文件:yml、yaml、properties,所以要将这一步与导入核心依赖分开
spring-boot-starter-parent
<resource>
<directory>${basedir}/src/main/resources</directory>
<filtering>true</filtering>
<includes>
<include>**/application*.yml</include>
<include>**/application*.yaml</include>
<include>**/application*.properties</include>
</includes>
</resource>
总结:
- spring-boot-dependencies:定义版本号
- spring-boot-starter-parent:加载资源文件yml、yaml、properties
- spring-boot-starter:导入我们要用的包