一、自动装配
1.1 @SpringBootApplication
此注解内容实现为多个注解的组合,相当于声明@Configuration
、 @EnableAutoConfiguration
和 @ComponentScan
。
@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.1.1 @SpringBootConfiguration
此注解内部实现为@Configuration
,标明当前类为一个配置类,加入到ioc容器中。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed
public @interface SpringBootConfiguration {
1.1.2 @ComponentScan
此注解为包扫描注解,如果不指定特定的扫描路径的话,扫描的路径是当前修饰的类所在的包及其子包。
1.1.3 @EnableAutoConfiguration
启用 Spring Application Context 的自动配置,是自动装配的核心。此注解同样是组合注解,由@AutoConfigurationPackage
和@Import()
注解组成。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
...
}
1. @AutoConfigurationPackage
使用AutoConfigurationPackages注册包。当没有指定base packages或base package classes时,注解类所在的包及子包被扫描,将其中的组件注册到容器中。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
可以看到,此注解中使用了`@Import`注解,并导入了`AutoConfigurationPackages.Registrar.class`类:
// ImportBeanDefinitionRegistrar用于存储来自导入配置的基本包
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
// 默认注册注解所在类所在包
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)
此注解中传入AutoConfigurationImportSelector.class
,其实现了ImportSelector
接口,重写了selectImports()
方法,我们清楚该方法的作用就是要返回需要注册到IoC容器中的对象对应的类型的全类路径名称的字符串数组。我们只需要关注此方法返回的内容,即可知道自动导入了哪些内容。
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
// 获取自动配置实体
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
(1) 获取自动配置实体信息
/**
* Return the {@link AutoConfigurationEntry} based on the {@link AnnotationMetadata}
* of the importing {@link Configuration @Configuration} class.
* @param annotationMetadata 配置类的注解元数据
* @return 应该导入的自动配置
*/
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
// 获取注解的属性信息
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 获取候选配置信息 加载的是 当前项目的classpath目录下的 所有的 spring.factories 文件中的 key 为
// org.springframework.boot.autoconfigure.EnableAutoConfiguration 的信息
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
// 因为会加载多个 spring.factories 文件,那么就有可能存在同名的,
// removeDuplicates方法的作用是 移除同名的
configurations = removeDuplicates(configurations);
// 获取我们配置的 exclude 信息
// @SpringBootApplication(exclude = {RabbitAutoConfiguration.class})
// 显示的指定不要加载那个配置类
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
// filter的作用是 过滤掉咱们不需要使用的配置类。
configurations = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
a. 获取候选配置数据
可以看到是读取的类路径下META-INF/spring.factories
文件中的内容。
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = new ArrayList<>(
SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()));
ImportCandidates.load(AutoConfiguration.class, getBeanClassLoader()).forEach(configurations::add);
Assert.notEmpty(configurations,
"No auto configuration classes found in META-INF/spring.factories nor in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. If you "
+ "are using a custom packaging, make sure that file is correct.");
return configurations;
}
返回SpringFactoriesLoader用于加载配置候选的类:
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
return EnableAutoConfiguration.class;
}
使用给定的类加载器从”META-INF/spring.factories”加载给定类型的工厂实现的完全限定类名:
//
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
ClassLoader classLoaderToUse = classLoader;
if (classLoaderToUse == null) {
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}
String factoryTypeName = factoryType.getName();
return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}
从类路径下的spring.properties
中读取:
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
Map<String, List<String>> result = cache.get(classLoader);
if (result != null) {
return result;
}
result = new HashMap<>();
try {
// public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryTypeName = ((String) entry.getKey()).trim();
String[] factoryImplementationNames =
StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
for (String factoryImplementationName : factoryImplementationNames) {
result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
.add(factoryImplementationName.trim());
}
}
}
// Replace all lists with unmodifiable lists containing unique elements
result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
.collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
cache.put(classLoader, result);
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
return result;
}
b. 移除重复的配置信息
protected final <T> List<T> removeDuplicates(List<T> list) {
return new ArrayList<>(new LinkedHashSet<>(list));
}
c. 移除排除项
获取排除项:
protected Set<String> getExclusions(AnnotationMetadata metadata, AnnotationAttributes attributes) {
Set<String> excluded = new LinkedHashSet<>();
excluded.addAll(asList(attributes, "exclude"));
excluded.addAll(Arrays.asList(attributes.getStringArray("excludeName")));
excluded.addAll(getExcludeAutoConfigurationsProperty());
return excluded;
}
private void checkExcludedClasses(List<String> configurations, Set<String> exclusions) {
List<String> invalidExcludes = new ArrayList<>(exclusions.size());
for (String exclusion : exclusions) {
if (ClassUtils.isPresent(exclusion, getClass().getClassLoader()) && !configurations.contains(exclusion)) {
invalidExcludes.add(exclusion);
}
}
if (!invalidExcludes.isEmpty()) {
handleInvalidExcludes(invalidExcludes);
}
}
d. 过滤不需要的配置类
getConfigurationClassFilter().filter(configurations);
调用此方法,对其中的内容进行过滤。
private static class ConfigurationClassFilter {
private final AutoConfigurationMetadata autoConfigurationMetadata;
private final List<AutoConfigurationImportFilter> filters;
ConfigurationClassFilter(ClassLoader classLoader, List<AutoConfigurationImportFilter> filters) {
// 此处加载自动配置元数据,读取类路径下META-INF/spring-autoconfigure-metadata.properties
this.autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(classLoader);
this.filters = filters;
}
List<String> filter(List<String> configurations) {
long startTime = System.nanoTime();
String[] candidates = StringUtils.toStringArray(configurations);
boolean skipped = false;
for (AutoConfigurationImportFilter filter : this.filters) {
// 此处匹配方法,对加载到的自动配置类信息,进行条件过滤,满足条件的才会导入
boolean[] match = filter.match(candidates, this.autoConfigurationMetadata);
for (int i = 0; i < match.length; i++) {
if (!match[i]) {
candidates[i] = null;
skipped = true;
}
}
}
if (!skipped) {
return configurations;
}
List<String> result = new ArrayList<>(candidates.length);
for (String candidate : candidates) {
if (candidate != null) {
result.add(candidate);
}
}
if (logger.isTraceEnabled()) {
int numberFiltered = configurations.size() - result.size();
logger.trace("Filtered " + numberFiltered + " auto configuration class in "
+ TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime) + " ms");
}
return result;
}
}
读取自动配置元数据:
protected static final String PATH = "META-INF/spring-autoconfigure-metadata.properties";
static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader) {
return loadMetadata(classLoader, PATH);
}
如mybatis的自动配置信息:
内容:
org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration=
org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration.AutoConfigureAfter=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration
// 存在SqlSessionFactory、SqlSessionFactoryBean才会注入
org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration.ConditionalOnClass=org.apache.ibatis.session.SqlSessionFactory,org.mybatis.spring.SqlSessionFactoryBean
org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration.ConditionalOnSingleCandidate=javax.sql.DataSource
org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration=
org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration$FreeMarkerConfiguration=
org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration$FreeMarkerConfiguration.ConditionalOnClass=org.mybatis.scripting.freemarker.FreeMarkerLanguageDriver,org.mybatis.scripting.freemarker.FreeMarkerLanguageDriverConfig
org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration$LegacyFreeMarkerConfiguration=
org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration$LegacyFreeMarkerConfiguration.ConditionalOnClass=org.mybatis.scripting.freemarker.FreeMarkerLanguageDriver
org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration$LegacyVelocityConfiguration=
org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration$LegacyVelocityConfiguration.ConditionalOnClass=org.mybatis.scripting.velocity.Driver
org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration$ThymeleafConfiguration=
org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration$ThymeleafConfiguration.ConditionalOnClass=org.mybatis.scripting.thymeleaf.ThymeleafLanguageDriver
org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration$VelocityConfiguration=
org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration$VelocityConfiguration.ConditionalOnClass=org.mybatis.scripting.velocity.VelocityLanguageDriver,org.mybatis.scripting.velocity.VelocityLanguageDriverConfig
org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration.ConditionalOnClass=org.apache.ibatis.scripting.LanguageDriver