一、自动装配
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@Indexedpublic @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 {@Overridepublic void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {// 默认注册注解所在类所在包register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));}@Overridepublic Set<Object> determineImports(AnnotationMetadata metadata) {return Collections.singleton(new PackageImports(metadata));}}
2. @Import(AutoConfigurationImportSelector.class)
此注解中传入AutoConfigurationImportSelector.class,其实现了ImportSelector接口,重写了selectImports()方法,我们清楚该方法的作用就是要返回需要注册到IoC容器中的对象对应的类型的全类路径名称的字符串数组。我们只需要关注此方法返回的内容,即可知道自动导入了哪些内容。
@Overridepublic 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 elementsresult.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.propertiesthis.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.SqlSessionFactoryBeanorg.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration.ConditionalOnSingleCandidate=javax.sql.DataSourceorg.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.FreeMarkerLanguageDriverConfigorg.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration$LegacyFreeMarkerConfiguration=org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration$LegacyFreeMarkerConfiguration.ConditionalOnClass=org.mybatis.scripting.freemarker.FreeMarkerLanguageDriverorg.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration$LegacyVelocityConfiguration=org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration$LegacyVelocityConfiguration.ConditionalOnClass=org.mybatis.scripting.velocity.Driverorg.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration$ThymeleafConfiguration=org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration$ThymeleafConfiguration.ConditionalOnClass=org.mybatis.scripting.thymeleaf.ThymeleafLanguageDriverorg.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration$VelocityConfiguration=org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration$VelocityConfiguration.ConditionalOnClass=org.mybatis.scripting.velocity.VelocityLanguageDriver,org.mybatis.scripting.velocity.VelocityLanguageDriverConfigorg.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration.ConditionalOnClass=org.apache.ibatis.scripting.LanguageDriver
