环境说明
本文是基于SpringBoot 2.5.2版本
从入口说起
@SpringBootApplication
public class StaffingSystemApplication {
public static void main(String[] args) {
SpringApplication.run(Demo.class, args);
}
}
可以看到,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 {
.......
}
1、@SpringBootConfiguration:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
......
}
可以看到,这个注解主要就是一个Configuration,用来标明SpringBootApplication是一个配置类
2、@ComponentScan显然就是一个包扫描的配置,用于扫描注册bean,默认会扫描启动类所在的包下的类,并默认排除TypeExcludeFilter和AutoConfigurationExcludeFilter两种自定义的bean类
3、自动装配的重点在这个@EnableAutoConfiguration注解上,核心代码如下:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
......
}
可以看到,这个注解上又有两个关键注解@AutoConfigurationPackage和@Import(AutoConfigurationImportSelector.class),首先@AutoConfigurationPackage的核心代码又如下:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
}
这个AutoConfigurationPackages.Registrar.class
是一个静态类,在源码的注释上有一段话:
/**
* {@link ImportBeanDefinitionRegistrar} to store the base package from the importing
* configuration.
*/
说明这个类主要是用来将@SpringBootApplication所在的包及其子包下的所有bean扫面注册到容器中
所以还有一个关键的注解:@Import(AutoConfigurationImportSelector.class),这个AutoConfigurationImportSelector类的继承体系如下:
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
}
public interface DeferredImportSelector extends ImportSelector {
}
public interface ImportSelector {
String[] selectImports(AnnotationMetadata var1);
}
AutoConfigurationImportSelector实现了ImportSelector接口,这个接口的主要作用是:
/**
* Select and return the names of which class(es) should be imported based on
* the {@link AnnotationMetadata} of the importing @{@link Configuration} class.
* @return the class names, or an empty array if none
*/
用中文理解大致就是指:
获取所有符合条件的bean类的全限定类名,这些类需要被加载到 IoC 容器中
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
因此,我们要重点看一下这个getAutoConfigurationEntry
方法
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
//判断配置文件中是否禁用了自动装配,默认spring.boot.enableautoconfiguration=true,
//可在 application.properties 或 application.yml 中设置
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
//获取注解中的属性
//debug下发现是获取@EnableAutoConfiguration注解中的 exclude 和 excludeName
AnnotationAttributes attributes = getAttributes(annotationMetadata);
//获取需要注入的类
//(*)
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
//移除重复、指定的一些类
//(**)
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = getConfigurationClassFilter().filter(configurations);
//(***)
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
上述getAutoConfigurationEntry
方法主要有以下几个关键点:
1、在
(*)
处,getCandidateConfigurations方法内部的核心代码为: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; } public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) { ...... String factoryTypeName = factoryType.getName(); return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList()); } private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) { ...... try { Enumeration urls = classLoader.getResources("META-INF/spring.factories"); } ...... }
可见,自动装配是通过读取
META-INF/spring.factories
文件来得到可能要加载的类的,所有的starter底下的这个文件都会被读取到(后面两处代码也不少,这里就不放代码了,就说说具体的思路)
2、在
(**)
处,通过之前得到的exclude 和 excludeName,以及spring.autoconfigure.exclude
配置的内容,移除一些不需要加载到容器的类3、在
(***)
处再次筛选,getConfigurationClassFilter()
方法得到一系列AutoConfigurationImportFilter
,这个filter接口的实现类有OnBeanCondition
、OnClassCondition
等,而注解@ConditionalOnBean、@ConditionalOnClass
等分别引用了这两个类,这样用这两个注解标识的类将会在容器内有指定bean、类路径有指定class的情况下才会注入到容器中,类似的注解其实还有很多,如@ConditionalOnMissingBean、@ConditionalOnMissingClass、@ConditionalOnProperty
等,都是基于上述的原理简而言之,就是
@ConditionalOnXXX
中需要满足指定的条件才能让该类注入到容器
自动配置总结
SpringBoot自动配置主要是通过@EnableAutoConfiguration
注解实现的,通过导入ImportSelector
接口的实现类AutoConfigurationImportSelector
,加载META-INF/spring.factories文件下的类,并通过exclude筛选掉不需要加载的类,以及用@Conditional
注解按需加载配置类。
因此也可以了解到,如果要自己实现一个starter自动注入,就需要在META-INF/spring.factories文件写入对应的类