一、自动装配

Spring Boot - 图1

1.1 @SpringBootApplication

此注解内容实现为多个注解的组合,相当于声明@Configuration@EnableAutoConfiguration@ComponentScan

  1. @Target(ElementType.TYPE)
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. @Inherited
  5. @SpringBootConfiguration
  6. @EnableAutoConfiguration
  7. @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
  8. @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
  9. public @interface SpringBootApplication {
  10. ...
  11. }

1.1.1 @SpringBootConfiguration

此注解内部实现为@Configuration,标明当前类为一个配置类,加入到ioc容器中。

  1. @Target(ElementType.TYPE)
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. @Configuration
  5. @Indexed
  6. public @interface SpringBootConfiguration {

1.1.2 @ComponentScan

此注解为包扫描注解,如果不指定特定的扫描路径的话,扫描的路径是当前修饰的类所在的包及其子包。

1.1.3 @EnableAutoConfiguration

启用 Spring Application Context 的自动配置,是自动装配的核心。此注解同样是组合注解,由@AutoConfigurationPackage@Import()注解组成。

  1. @Target(ElementType.TYPE)
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. @Inherited
  5. @AutoConfigurationPackage
  6. @Import(AutoConfigurationImportSelector.class)
  7. public @interface EnableAutoConfiguration {
  8. ...
  9. }

1. @AutoConfigurationPackage

使用AutoConfigurationPackages注册包。当没有指定base packages或base package classes时,注解类所在的包及子包被扫描,将其中的组件注册到容器中。

  1. @Target(ElementType.TYPE)
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. @Inherited
  5. @Import(AutoConfigurationPackages.Registrar.class)
  6. public @interface AutoConfigurationPackage {
  1. 可以看到,此注解中使用了`@Import`注解,并导入了`AutoConfigurationPackages.Registrar.class`类:
  1. // ImportBeanDefinitionRegistrar用于存储来自导入配置的基本包
  2. static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
  3. @Override
  4. public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
  5. // 默认注册注解所在类所在包
  6. register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
  7. }
  8. @Override
  9. public Set<Object> determineImports(AnnotationMetadata metadata) {
  10. return Collections.singleton(new PackageImports(metadata));
  11. }
  12. }

2. @Import(AutoConfigurationImportSelector.class)

此注解中传入AutoConfigurationImportSelector.class,其实现了ImportSelector接口,重写了selectImports()方法,我们清楚该方法的作用就是要返回需要注册到IoC容器中的对象对应的类型的全类路径名称的字符串数组。我们只需要关注此方法返回的内容,即可知道自动导入了哪些内容。

  1. @Override
  2. public String[] selectImports(AnnotationMetadata annotationMetadata) {
  3. if (!isEnabled(annotationMetadata)) {
  4. return NO_IMPORTS;
  5. }
  6. // 获取自动配置实体
  7. AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
  8. return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
  9. }

(1) 获取自动配置实体信息
  1. /**
  2. * Return the {@link AutoConfigurationEntry} based on the {@link AnnotationMetadata}
  3. * of the importing {@link Configuration @Configuration} class.
  4. * @param annotationMetadata 配置类的注解元数据
  5. * @return 应该导入的自动配置
  6. */
  7. protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
  8. if (!isEnabled(annotationMetadata)) {
  9. return EMPTY_ENTRY;
  10. }
  11. // 获取注解的属性信息
  12. AnnotationAttributes attributes = getAttributes(annotationMetadata);
  13. // 获取候选配置信息 加载的是 当前项目的classpath目录下的 所有的 spring.factories 文件中的 key 为
  14. // org.springframework.boot.autoconfigure.EnableAutoConfiguration 的信息
  15. List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
  16. // 因为会加载多个 spring.factories 文件,那么就有可能存在同名的,
  17. // removeDuplicates方法的作用是 移除同名的
  18. configurations = removeDuplicates(configurations);
  19. // 获取我们配置的 exclude 信息
  20. // @SpringBootApplication(exclude = {RabbitAutoConfiguration.class})
  21. // 显示的指定不要加载那个配置类
  22. Set<String> exclusions = getExclusions(annotationMetadata, attributes);
  23. checkExcludedClasses(configurations, exclusions);
  24. configurations.removeAll(exclusions);
  25. // filter的作用是 过滤掉咱们不需要使用的配置类。
  26. configurations = getConfigurationClassFilter().filter(configurations);
  27. fireAutoConfigurationImportEvents(configurations, exclusions);
  28. return new AutoConfigurationEntry(configurations, exclusions);
  29. }

a. 获取候选配置数据

可以看到是读取的类路径下META-INF/spring.factories文件中的内容。

  1. protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
  2. List<String> configurations = new ArrayList<>(
  3. SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()));
  4. ImportCandidates.load(AutoConfiguration.class, getBeanClassLoader()).forEach(configurations::add);
  5. Assert.notEmpty(configurations,
  6. "No auto configuration classes found in META-INF/spring.factories nor in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. If you "
  7. + "are using a custom packaging, make sure that file is correct.");
  8. return configurations;
  9. }

返回SpringFactoriesLoader用于加载配置候选的类:

  1. protected Class<?> getSpringFactoriesLoaderFactoryClass() {
  2. return EnableAutoConfiguration.class;
  3. }

使用给定的类加载器从”META-INF/spring.factories”加载给定类型的工厂实现的完全限定类名:

  1. //
  2. public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
  3. ClassLoader classLoaderToUse = classLoader;
  4. if (classLoaderToUse == null) {
  5. classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
  6. }
  7. String factoryTypeName = factoryType.getName();
  8. return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
  9. }

从类路径下的spring.properties中读取:

  1. private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
  2. Map<String, List<String>> result = cache.get(classLoader);
  3. if (result != null) {
  4. return result;
  5. }
  6. result = new HashMap<>();
  7. try {
  8. // public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
  9. Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
  10. while (urls.hasMoreElements()) {
  11. URL url = urls.nextElement();
  12. UrlResource resource = new UrlResource(url);
  13. Properties properties = PropertiesLoaderUtils.loadProperties(resource);
  14. for (Map.Entry<?, ?> entry : properties.entrySet()) {
  15. String factoryTypeName = ((String) entry.getKey()).trim();
  16. String[] factoryImplementationNames =
  17. StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
  18. for (String factoryImplementationName : factoryImplementationNames) {
  19. result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
  20. .add(factoryImplementationName.trim());
  21. }
  22. }
  23. }
  24. // Replace all lists with unmodifiable lists containing unique elements
  25. result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
  26. .collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
  27. cache.put(classLoader, result);
  28. }
  29. catch (IOException ex) {
  30. throw new IllegalArgumentException("Unable to load factories from location [" +
  31. FACTORIES_RESOURCE_LOCATION + "]", ex);
  32. }
  33. return result;
  34. }

b. 移除重复的配置信息
  1. protected final <T> List<T> removeDuplicates(List<T> list) {
  2. return new ArrayList<>(new LinkedHashSet<>(list));
  3. }

c. 移除排除项

获取排除项:

  1. protected Set<String> getExclusions(AnnotationMetadata metadata, AnnotationAttributes attributes) {
  2. Set<String> excluded = new LinkedHashSet<>();
  3. excluded.addAll(asList(attributes, "exclude"));
  4. excluded.addAll(Arrays.asList(attributes.getStringArray("excludeName")));
  5. excluded.addAll(getExcludeAutoConfigurationsProperty());
  6. return excluded;
  7. }
  1. private void checkExcludedClasses(List<String> configurations, Set<String> exclusions) {
  2. List<String> invalidExcludes = new ArrayList<>(exclusions.size());
  3. for (String exclusion : exclusions) {
  4. if (ClassUtils.isPresent(exclusion, getClass().getClassLoader()) && !configurations.contains(exclusion)) {
  5. invalidExcludes.add(exclusion);
  6. }
  7. }
  8. if (!invalidExcludes.isEmpty()) {
  9. handleInvalidExcludes(invalidExcludes);
  10. }
  11. }

d. 过滤不需要的配置类

getConfigurationClassFilter().filter(configurations);调用此方法,对其中的内容进行过滤。

  1. private static class ConfigurationClassFilter {
  2. private final AutoConfigurationMetadata autoConfigurationMetadata;
  3. private final List<AutoConfigurationImportFilter> filters;
  4. ConfigurationClassFilter(ClassLoader classLoader, List<AutoConfigurationImportFilter> filters) {
  5. // 此处加载自动配置元数据,读取类路径下META-INF/spring-autoconfigure-metadata.properties
  6. this.autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(classLoader);
  7. this.filters = filters;
  8. }
  9. List<String> filter(List<String> configurations) {
  10. long startTime = System.nanoTime();
  11. String[] candidates = StringUtils.toStringArray(configurations);
  12. boolean skipped = false;
  13. for (AutoConfigurationImportFilter filter : this.filters) {
  14. // 此处匹配方法,对加载到的自动配置类信息,进行条件过滤,满足条件的才会导入
  15. boolean[] match = filter.match(candidates, this.autoConfigurationMetadata);
  16. for (int i = 0; i < match.length; i++) {
  17. if (!match[i]) {
  18. candidates[i] = null;
  19. skipped = true;
  20. }
  21. }
  22. }
  23. if (!skipped) {
  24. return configurations;
  25. }
  26. List<String> result = new ArrayList<>(candidates.length);
  27. for (String candidate : candidates) {
  28. if (candidate != null) {
  29. result.add(candidate);
  30. }
  31. }
  32. if (logger.isTraceEnabled()) {
  33. int numberFiltered = configurations.size() - result.size();
  34. logger.trace("Filtered " + numberFiltered + " auto configuration class in "
  35. + TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime) + " ms");
  36. }
  37. return result;
  38. }
  39. }
  1. 读取自动配置元数据:
  1. protected static final String PATH = "META-INF/spring-autoconfigure-metadata.properties";
  2. static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader) {
  3. return loadMetadata(classLoader, PATH);
  4. }

如mybatis的自动配置信息:
image.png
内容:

  1. org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration=
  2. org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration.AutoConfigureAfter=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration
  3. // 存在SqlSessionFactory、SqlSessionFactoryBean才会注入
  4. org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration.ConditionalOnClass=org.apache.ibatis.session.SqlSessionFactory,org.mybatis.spring.SqlSessionFactoryBean
  5. org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration.ConditionalOnSingleCandidate=javax.sql.DataSource
  6. org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration=
  7. org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration$FreeMarkerConfiguration=
  8. org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration$FreeMarkerConfiguration.ConditionalOnClass=org.mybatis.scripting.freemarker.FreeMarkerLanguageDriver,org.mybatis.scripting.freemarker.FreeMarkerLanguageDriverConfig
  9. org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration$LegacyFreeMarkerConfiguration=
  10. org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration$LegacyFreeMarkerConfiguration.ConditionalOnClass=org.mybatis.scripting.freemarker.FreeMarkerLanguageDriver
  11. org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration$LegacyVelocityConfiguration=
  12. org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration$LegacyVelocityConfiguration.ConditionalOnClass=org.mybatis.scripting.velocity.Driver
  13. org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration$ThymeleafConfiguration=
  14. org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration$ThymeleafConfiguration.ConditionalOnClass=org.mybatis.scripting.thymeleaf.ThymeleafLanguageDriver
  15. org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration$VelocityConfiguration=
  16. org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration$VelocityConfiguration.ConditionalOnClass=org.mybatis.scripting.velocity.VelocityLanguageDriver,org.mybatis.scripting.velocity.VelocityLanguageDriverConfig
  17. org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration.ConditionalOnClass=org.apache.ibatis.scripting.LanguageDriver