1、pom.xml分析
核心依赖
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.5</version>
<!--父工程-->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.4.5</version>
spring-boot-dependencies是spring-boot-starter-parent的父工程,所有的核心依赖都在父工程中,所以我们在引入Spring Boot依赖的时候,不需要指定版本,就因为有这些版本仓库。
启动器:可以理解为Spring Boot的启动场景
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
- 比如:spring-boot-starter-web,就会帮我们自动导入web环境所有的依赖
- Spring Boot会将所有的功能场景,变成一个个的启动器
- 我们要使用什么功能,就只需要找到对应的启动器(starter)就可以了
2、@SpringBootApplication分析
启动类:
package com.cheng;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Springboot01HelloworldApplication {
public static void main(String[] args) {
//将Spring Boot应用启动
SpringApplication.run(Springboot01HelloworldApplication.class, args);//反射机制
}
}
@SpringBootApplication
@SpringBootApplication 标注在某个类上说明这个类是SpringBoot的主配置类 , 正是通过添加了@SpringBootApplication这个注解SpringBoot应用才能启动;
@SpringBootApplication是一个“三体”结构,实际上它是一个复合 注解,点进去查看@SpringBootApplication注解组成部分:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
虽然它的定义使用了多个 注解进行元信息标注,但实际上对于 SpringBoot 应用来说,重要的只有三个 注解,而“三体”结构实际上指的就是这三个 Annotation:
@SpringBootConfiguration:这个注解标注在某类上,说明这个类是一个springboot配置类@EnableAutoConfiguration:这个注解就是springboot能实现自动配置的关键,很重要@ComponentScan:扫描主启动类同级目录的包,把扫描到的类装配到spring容器中,交给spring容器托管
分析@SpringBootConfiguration
@SpringBootConfiguration源码里只有两个重要注解
@Configuration:说明启动类是spring配置类@Component:说明这也是一个spring组件
详细分析@EnableAutoConfiguration
点击查看@EnableAutoConfiguration的组成,发现它也是一个组合注解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
Spring源码中有很多以Enable开头的注解,其作用就是借助@Import注解来收集并注册特定场景相关的bean,并加载到spring容器。
@EnableAutoConfiguration的作用就是借助@Import注解来收集所有符合自动配置条件的bean定义,并加载到spring容器。
@EnableAutoConfiguration组合注解中两个重要的注解:
-
==第一个:
@AutoConfigurationPackage==:自动配置包,其源码如下@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @Import(AutoConfigurationPackages.Registrar.class) public @interface AutoConfigurationPackage {发现
@AutoConfigurationPackage又是一个组合注解,其中最重要的注解是:@Import(AutoConfigurationPackages.Registrar.class):==@Import是Spring的底层注解,表示给容器中导入一个组件==;例如@Import(AutoConfigurationPackages.Registrar.class),它就是将Registrar这个组件类注册到容器中,可查看Registrar类中registerBeanDefinitions方法,这个方法就是导入组件类的具体实现@Override //AnnotationMetadata(注解标注的元信息中包含了使用了哪些注解、相应的注解作用在哪个类上) public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { //将注解标注的元信息传入,获取到相应的包名 register(registry, new PackageImport(metadata).getPackageName()); }@AutoConfigurationPackage注解就是将主配置类(@SpringBootApplication)标注的所有包及子包里面的所有组件扫描到Spring容器当中。所以在使用SpringBoot 开发项目的时候,创建类的时候需要在(
@SpringBootApplication)标注的所有包及子包里面创建,这样SpringBoot 就能把所有的组件加入到Spring容器当中。 -
**==第二个:==
@Import(AutoConfigurationImportSelector.class):**将AutoConfigurationImportSelector这个类导入到spring容器中,AutoConfigurationImportSelector可以帮助springboot应用将所有符合条件的@Configuration配置都加载到当前SpringBoot创建并使用的spring容器(ApplicationContext)中。查看AutoConfigurationImportSelector源码,通过源码分析这个类是通过selectImports方法告诉Spring Boot都需要导入哪些组件
@Override public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return NO_IMPORTS; } //获得自动配置实体 AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata); return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); }继续分析AutoConfigurationImportSelector源码
点进去查看getCandidateConfigurations方法
从下面源码中可以看出SpringBoot 在启动的时候从类路径下的 META-INF/spring.factores 中获取EnableAutoConfiguration指定的值(也就是启动器),将这些作为自动配置类导入到容器当中,自动配置类就生效,帮我们进行自动配置的工作
//获取候选配置
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
。
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
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;
}
getCandidateConfigurations方法有一个重要的方法loadFactoryNames,这个方法需要传入两个参数:
getSpringFactoriesLoaderFactoryClass()和getBeanClassLoader()
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
return EnableAutoConfiguration.class;
}
protected ClassLoader getBeanClassLoader() {
return this.beanClassLoader;
}
-
getSpringFactoriesLoaderFactoryClass()这个方法返回的是EnableAutoConfiguration.class
-
getBeanClassLoader()这个方法返回的是beanClassLoader(类加载器)
再点进去查看loadFactoryNames方法和loadSpringFactories方法的源码:
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
//获取出入的键
String factoryClassName = factoryClass.getName();
return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
if (result != null) {
return result;
} else {
try {
//加载类路径下FACTORIES_RESOURCE_LOCATION文件,将其中设置的配置类的全路径信息封装为Enumeration类对象
Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
//循环Enumeration类对象,根据相应的节点信息生成Properties对象,通过传入的键获取值,在将值切割为一个个小的字符串转化为Array,方法result集合中
while(urls.hasMoreElements()) {
URL url = (URL)urls.nextElement();
UrlResource resource = new UrlResource(url);
//所有的资源加载到配置类中
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
Iterator var6 = properties.entrySet().iterator();
while(var6.hasNext()) {
Entry<?, ?> entry = (Entry)var6.next();
String factoryClassName = ((String)entry.getKey()).trim();
String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
int var10 = var9.length;
for(int var11 = 0; var11 < var10; ++var11) {
String factoryName = var9[var11];
result.add(factoryClassName, factoryName.trim());
}
}
}
cache.put(classLoader, result);
return result;
关键功能由@Import提供,其导入的AutoConfigurationImportSelector的selectImports()方法通过SpringFactoriesLoader.loadFactoryNames()扫描所有具有META-INF/spring.factories的jar包。spring-boot-autoconfigure-x.x.x.x.jar里就有一个这样的spring.factories文件。这个文件里面有所有的自动配置器类。
、
@Import(AutoConfigurationImportSelector.class)实现的是SpringBoot 在启动的时候从类路径下的 META-INF/spring.factores 中获取EnableAutoConfiguration指定的值,将这些作为自动配置类导入到容器当中,自动配置类就生效,帮我们进行自动配置的工作。
总结:Spring Boot所有自动配置类都是在启动的时候扫描并加载,所有自动配置类都在spring-boot-autoconfigure-2.4.5.jar-->META-INF目录的spring.factories文件里,但不一定都生效,要看是否导入了start,只要导入了对应的start,就有对应的启动器了,有了启动器,我们的自动装配类就会生效,然后就自动配置成功。
自动装配原理分析图:
由于本人能力有限,若文章有错误的地方,请大家指出,一起交流学习。