SpringBoot自动配置原理分析
springboot的重点是starter和autoconfiguration
版本号:springboot 2.4.3
学习顺序:
- 先了解springboot的starter
- 学习注解的作用
- 自动配置的原理
1.依赖管理
- 每个springboot项目都有spring-boot-starter-parent父依赖,但spring-boot-starter-parent中并没有依赖的版本号
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
- 版本号在spring-boot-starter-parent的父项目中,
<properties>...</properties>
来规定版本号
-<dependencyManagement>...</dependencyManagement>
作为依赖管理,不能被继承,只可以引用
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.4.3</version>
</parent>
-
starter场景启动器
官方场景启动器:spring-boot-starter-*
第三方场景启动器:*-spring-boot-starter
所有的场景启动器都需要依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.4.3</version>
<scope>compile</scope>
</dependency>
2.注解的作用
2.1 @Configuration
配置类也是组件,组件既对象的意思
因为@Configuration中包含@Component
- 自定义配置类,使用@Bean标注在方法上给容器注册组件,默认也是单实例的。示例:
//自定义配置类
@Configuration
public class MyConfig {
@Bean //给容器中添加组件,以方法名作为组件的id,返回类型就是组件类型。返回的值就是组件在容器中的实例
public User user(){
return new User("zhangsan",18);
}
}
//测试代码
@SpringBootApplication
public class SpringbootFasterApplication {
public static void main(String[] args) {
ConfigurableApplicationContext run = SpringApplication.run(SpringbootFasterApplication.class, args);
//从Spring容器中拿到两个User实例,判断对象是否相等,来判断是否为单实例
User user1 = run.getBean("user", User.class);
User user2 = run.getBean("user", User.class);
System.out.println("组件:"+(user1==user2));
}
}
输出结果:
组件:true
```
- proxyBeanMethods:代理bean的方法,默认为true
Full(proxyBeanMethods = true)用处:保证每个@Bean方法每次被调用返回的组件都是单实例的
Lite(proxyBeanMethods = false)用处:每个@Bean方法每次被调返回的组件都是新创建的
示例:
//配置类中加入proxyBeanMethods = false
@Configuration(proxyBeanMethods = false)
public class MyConfig {
@Bean //给容器中添加组件,以方法名作为组件的id,返回类型就是组件类型。返回的值就是组件在容器中的实例
public User user(){
return new User("zhangsan",18);
}
}
@SpringBootApplication
public class SpringbootFasterApplication {
public static void main(String[] args) {
ConfigurableApplicationContext run = SpringApplication.run(SpringbootFasterApplication.class, args);
//从Spring容器中获取myConfig对象,调用MyConfig中的方法,Full(proxyBeanMethods = false时,每次的组件都是新创建的
MyConfig myConfig = run.getBean(MyConfig.class);
User user1 = myConfig.user();
User user2=myConfig.user();
System.out.println("组件:"+(user1==user2));
}
}
输出结果:
组件:false
2.2 创建组件的几种注解
@Bean、@Component、@Controller@Service、@Repository
这里新加个@Import注解,可以给容器中自动创建出某个类型的组件、默认组件的名字为全类名。可以使Springboot里自带的类,只需要导入字节码文件。示例:
@Import({User.class})
@Configuration
public class MyConfig {
@Bean
public User user(){
return new User("zhangsan",18);
}
}
2.3 条件装配@Conditional
满足Conditional指定的条件,则进行组件注入
2.4 @ImportResource
可以导入Spring原生配置文件
@ImportResource(“classpath:beans.xml”)
public class MyConfig {}
2.5 配置绑定@ConfigurationProperties
application.properties配置文件
server.name=mycat
@ConfigurationProperties(prefix = "server")
public class Cat {}
//1、开启Cat配置绑定功能
//2、把这个Cat这个组件自动注册到容器中
@EnableConfigurationProperties(Cat.class)
public class MyConfig {
@Bean
public User user(){
return new User("zhangsan",18);
}
}
@SpringBootApplication
public class SpringbootFasterApplication {
public static void main(String[] args) {
ConfigurableApplicationContext run = SpringApplication.run(SpringbootFasterApplication.class, args);
Cat cat =(Cat) run.getBean(Cat.class);
System.out.println(cat);
}
}
输出结果:
Cat{name='mycat', age=0}
3.自动配置原理
@SpringBootApplication包含三个注解:
- @SpringBootConfiguration 代表当前是一个配置类
- @EnableAutoConfiguration(重要)
- @ComponentScan 制定扫描哪些
@EnableAutoConfiguration包含:
- @AutoConfigurationPackage
- @Import({AutoConfigurationImportSelector.class})
3.1 @AutoConfigurationPackage
作用:
- 利用Registrar给容器中导入一系列组件
- 将指定的一个包下的所有组件导入进来。
源码分析: 断点调试
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {}
//点开@AutoConfigurationPackage
@Import({Registrar.class})
public @interface AutoConfigurationPackage {}
点开Registrar.class
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
Registrar() {
}
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
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));
}
}
AnnotationMetadata metadata代表注解的原信息,@Import({Registrar.class})这个注解标注在AutoConfigurationPackage上,相当于标注在主引导类SpringbootFasterApplication上
结论:将包名为com.yulong下的所有组件导入到spring 容器中
3.2@Import(AutoConfigurationImportSelector.class)
利用getAutoConfigurationEntry(annotationMetadata)给容器中批量导入一些组件
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
}
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) {
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;
}
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
ClassLoader classLoaderToUse = classLoader;
if (classLoader == null) {
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}
String factoryTypeName = factoryType.getName();
return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
Enumeration urls = classLoader.getResources("META-INF/spring.factories");
}
-
利用getAutoConfigurationEntry(annotationMetadata);给容器中批量导入一些组件
-
调用List configurations =
getCandidateConfigurations(annotationMetadata, attributes)获取到所有需要导入到容器中的配置类 -
利用工厂加载 Map<String, List> loadSpringFactories(@NullableClassLoader classLoader);得到所有的组件
-
从META-INF/spring.factories位置来加载一个文件。
默认扫描我们当前系统里面所有META-INF/spring.factories位置的文件
spring-boot-autoconfigure-2.4.3.RELEASE.jar包里面也有META-INF/spring.factories虽然我们133个场景的所有自动配置启动的时候默认全部加载。xxxxAutoConfiguration
按照条件装配规则(@Conditional),最终会按需配置。
starter场景=》xxxAutoConfiguration=》通过xxxAutoConfiguration中的@Bean导入xxx组件=》绑定xxxProperties=》绑定配置文件项