返回介绍

Spring 系列

MyBatis

Netty

Dubbo

Tomcat

Redis

Nacos

Sentinel

RocketMQ

番外篇(JDK 1.8)

学习心得

SpringBoot 自动装配

发布于 2024-05-19 21:34:34 字数 24765 浏览 0 评论 0 收藏 0

Spring Boot 自动装配

@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 {

    @AliasFor(annotation = EnableAutoConfiguration.class)
    Class<?>[] exclude() default {};

    @AliasFor(annotation = EnableAutoConfiguration.class)
    String[] excludeName() default {};

    @AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
    String[] scanBasePackages() default {};

    @AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
    Class<?>[] scanBasePackageClasses() default {};

    @AliasFor(annotation = Configuration.class)
    boolean proxyBeanMethods() default true;

}

EnableAutoConfiguration

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

}

AutoConfigurationImportSelector

  • 类图

image-20200320150642022

getAutoConfigurationMetadata()

        @Override
        public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
            Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector,
                    () -> String.format("Only %s implementations are supported, got %s",
                            AutoConfigurationImportSelector.class.getSimpleName(),
                            deferredImportSelector.getClass().getName()));
            AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
                    .getAutoConfigurationEntry(
                            // 加载配置元数据
                            getAutoConfigurationMetadata(), annotationMetadata);
            this.autoConfigurationEntries.add(autoConfigurationEntry);
            for (String importClassName : autoConfigurationEntry.getConfigurations()) {
                this.entries.putIfAbsent(importClassName, annotationMetadata);
            }
        }


        private AutoConfigurationMetadata getAutoConfigurationMetadata() {
            if (this.autoConfigurationMetadata == null) {
                // 加载配置信息
                this.autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
            }
            return this.autoConfigurationMetadata;
        }
  • org.springframework.boot.autoconfigure.AutoConfigurationMetadataLoader#loadMetadata(java.lang.ClassLoader)

        static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader, String path) {
            try {
    
                // 获取资源路径
                Enumeration<URL> urls = (classLoader != null) ? classLoader.getResources(path)
                        : ClassLoader.getSystemResources(path);
                Properties properties = new Properties();
                while (urls.hasMoreElements()) {
                    properties.putAll(PropertiesLoaderUtils.loadProperties(new UrlResource(urls.nextElement())));
                }
                return loadMetadata(properties);
            }
            catch (IOException ex) {
                throw new IllegalArgumentException("Unable to load @ConditionalOnClass location [" + path + "]", ex);
            }
        }
    

    image-20200320160423991

  • protected static final String PATH = "META-INF/spring-autoconfigure-metadata.properties";

    注意: 这个文件在target编译后的文件夹中

    相关 Issues : https://github.com/spring-projects/spring-boot/issues/11282

  • 自动装配

    spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories

    该文件内存有:

    # Auto Configure
    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
    org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
    org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
    org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
    org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
    org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
    

image-20200320162835665

同样找一下 redis

image-20200320163001728

  • 仔细看org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration

先说注解

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })

EnableConfigurationProperties

自动映射一个POJO到Spring Boot配置文件(默认是application.properties文件)的属性集。

  • org.springframework.boot.autoconfigure.data.redis.RedisProperties
  • 部分 redis 配置属性
@ConfigurationProperties(prefix = "spring.redis")
public class RedisProperties {

    /**
     * Database index used by the connection factory.
     */
    private int database = 0;

    /**
     * Connection URL. Overrides host, port, and password. User is ignored. Example:
     * redis://user:password@example.com:6379
     */
    private String url;

    /**
     * Redis server host.
     */
    private String host = "localhost";

    /**
     * Login password of the redis server.
     */
    private String password;

    /**
     * Redis server port.
     */
    private int port = 6379;

    /**
     * Whether to enable SSL support.
     */
    private boolean ssl;

    /**
     * Connection timeout.
     */
    private Duration timeout;

    /**
     * Client name to be set on connections with CLIENT SETNAME.
     */
    private String clientName;



}
  • 找到一个我们用相同方式去寻找到别的一些属性处理如org.springframework.boot.autoconfigure.jdbc.JdbcProperties 具体展开请各位读者自行了解了

AnnotationMetadata

回过头继续我们的主要流程

  • org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.AutoConfigurationGroup#process

    image-20200320163806852

再此之前我们看过了getAutoConfigurationMetadata()的相关操作

关注 AnnotationMetadata annotationMetadata 存储了一些什么

image-20200320164145286

这里简单理解

  1. mergedAnnotations 类相关的注解信息
  2. annotationTypes 在启动类上的注解列表

getAutoConfigurationEntry

    protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
            AnnotationMetadata annotationMetadata) {
        if (!isEnabled(annotationMetadata)) {
            return EMPTY_ENTRY;
        }
        // 获取注解属性值
        AnnotationAttributes attributes = getAttributes(annotationMetadata);
        // 获取候选配置信息
        List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
        // 删除重复配置
        configurations = removeDuplicates(configurations);
        // 获取 exclude 属性
        Set<String> exclusions = getExclusions(annotationMetadata, attributes);
        // 校验 exclude 类
        checkExcludedClasses(configurations, exclusions);
        // 配置中删除 exclude 的属性值
        configurations.removeAll(exclusions);
        // 过滤
        configurations = filter(configurations, autoConfigurationMetadata);
        // 触发自动配置事件
        fireAutoConfigurationImportEvents(configurations, exclusions);
        // 返回
        return new AutoConfigurationEntry(configurations, exclusions);
    }

getAttributes

    protected AnnotationAttributes getAttributes(AnnotationMetadata metadata) {
        // name = org.springframework.boot.autoconfigure.EnableAutoConfiguration , 这是一个固定的值
        String name = getAnnotationClass().getName();
        // 获取注解的属性
        AnnotationAttributes attributes = AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(name, true));
        Assert.notNull(attributes, () -> "No auto-configuration attributes found. Is " + metadata.getClassName()
                + " annotated with " + ClassUtils.getShortName(name) + "?");
        return attributes;
    }

image-20200320171138431

getCandidateConfigurations

  • 读取spring.factories数据
    protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        // 读取 org.springframework.boot.autoconfigure.EnableAutoConfiguration 相关配置
        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;
    }

image-20200320171734270

  • 第一个是我自己写的一个测试用

    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
      org.sourcehot.service.HelloServiceAutoConfiguration
    

removeDuplicates

  • new 两个对象直接做数据转换,去重
    protected final <T> List<T> removeDuplicates(List<T> list) {
        return new ArrayList<>(new LinkedHashSet<>(list));
    }

getExclusions

    protected Set<String> getExclusions(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        Set<String> excluded = new LinkedHashSet<>();
        // 获取属性 exclude 值转换成list
        excluded.addAll(asList(attributes, "exclude"));
      // 获取属性 excludeName 值转换成list
        excluded.addAll(Arrays.asList(attributes.getStringArray("excludeName")));
        // 获取 SpringBoot 本身的忽略配置属性
        excluded.addAll(getExcludeAutoConfigurationsProperty());
        return excluded;
    }

getExcludeAutoConfigurationsProperty

    private List<String> getExcludeAutoConfigurationsProperty() {
        if (getEnvironment() instanceof ConfigurableEnvironment) {
            Binder binder = Binder.get(getEnvironment());
            // 取出 "spring.autoconfigure.exclude" 转换成list
            return binder.bind(PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE, String[].class).map(Arrays::asList)
                    .orElse(Collections.emptyList());
        }
        String[] excludes = getEnvironment().getProperty(PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE, String[].class);
        return (excludes != null) ? Arrays.asList(excludes) : Collections.emptyList();
    }

image-20200323080611527

  • 修改启动类

    @SpringBootApplication(excludeName =  { "org.sourcehot.service.HelloServiceAutoConfiguration" })
    

    image-20200323081009823

checkExcludedClasses

    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);
        }
    }
  • configurations.removeAll(exclusions)

    移除忽略的类

filter

    private List<String> filter(List<String> configurations, AutoConfigurationMetadata autoConfigurationMetadata) {
        long startTime = System.nanoTime();
        String[] candidates = StringUtils.toStringArray(configurations);
        boolean[] skip = new boolean[candidates.length];
        boolean skipped = false;
        // 获取 AutoConfigurationImportFilter 相关配置
        for (AutoConfigurationImportFilter filter : getAutoConfigurationImportFilters()) {
            // 执行 aware 相关接口
            invokeAwareMethods(filter);
            // 比较
            boolean[] match = filter.match(candidates, autoConfigurationMetadata);
            for (int i = 0; i < match.length; i++) {
                if (!match[i]) {
                    skip[i] = true;
                    candidates[i] = null;
                    skipped = true;
                }
            }
        }
        if (!skipped) {
            return configurations;
        }
        List<String> result = new ArrayList<>(candidates.length);
        for (int i = 0; i < candidates.length; i++) {
            if (!skip[i]) {
                result.add(candidates[i]);
            }
        }
        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 new ArrayList<>(result);
    }
  • getAutoConfigurationImportFilters()spring.factories 获取 AutoConfigurationImportFilter的接口

image-20200323081903145

  • 循环内执行Aware系列接口

match方法: org.springframework.boot.autoconfigure.AutoConfigurationImportFilter#match

  • filter.match(candidates, autoConfigurationMetadata) 比较判断哪些是需要自动注入的类

image-20200323082553595

fireAutoConfigurationImportEvents

    private void fireAutoConfigurationImportEvents(List<String> configurations, Set<String> exclusions) {
        // 获取自动配置的监听器列表
        List<AutoConfigurationImportListener> listeners = getAutoConfigurationImportListeners();
        if (!listeners.isEmpty()) {
            // 创建 自动配置事件
            AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this, configurations, exclusions);
            for (AutoConfigurationImportListener listener : listeners) {
                // 执行 Aware 相关接口
                invokeAwareMethods(listener);
                // 监听器执行自动配置事件
                listener.onAutoConfigurationImportEvent(event);
            }
        }
    }

image-20200323083149737

  • AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this, configurations, exclusions);

image-20200323083247061

  • org.springframework.boot.autoconfigure.AutoConfigurationImportListener#onAutoConfigurationImportEvent 在执行自动配置时触发 , 实现类只有 ConditionEvaluationReportAutoConfigurationImportListener

        @Override
        public void onAutoConfigurationImportEvent(AutoConfigurationImportEvent event) {
            if (this.beanFactory != null) {
                ConditionEvaluationReport report = ConditionEvaluationReport.get(this.beanFactory);
                // 记录需要加载的配置
                report.recordEvaluationCandidates(event.getCandidateConfigurations());
                // 记录不需要加载的配置
                report.recordExclusions(event.getExclusions());
            }
        }
    

image-20200323083656670

  • 初始化完

process

  • org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.AutoConfigurationGroup#process

image-20200323084922159

  • 后续的一些行为相对简单,直接放个源码了.
@Override
        public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
            Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector,
                    () -> String.format("Only %s implementations are supported, got %s",
                            AutoConfigurationImportSelector.class.getSimpleName(),
                            deferredImportSelector.getClass().getName()));
            // 自动装配信息
            AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
                    .getAutoConfigurationEntry(
                            // 加载配置元数据
                            getAutoConfigurationMetadata(), annotationMetadata);
            this.autoConfigurationEntries.add(autoConfigurationEntry);
            // 循环需要自动注入的类
            for (String importClassName : autoConfigurationEntry.getConfigurations()) {
                // 继续放入k,v
                this.entries.putIfAbsent(importClassName, annotationMetadata);
            }
        }

selectImports

        @Override
        public Iterable<Entry> selectImports() {
            if (this.autoConfigurationEntries.isEmpty()) {
                return Collections.emptyList();
            }
            // 获取忽略的类
            Set<String> allExclusions = this.autoConfigurationEntries.stream()
                    .map(AutoConfigurationEntry::getExclusions).flatMap(Collection::stream).collect(Collectors.toSet());

            // 获取需要注入的类
            Set<String> processedConfigurations = this.autoConfigurationEntries.stream()
                    .map(AutoConfigurationEntry::getConfigurations).flatMap(Collection::stream)
                    .collect(Collectors.toCollection(LinkedHashSet::new));

            // 把不需要自动注入的类从需要注入的类中移除
            processedConfigurations.removeAll(allExclusions);

            // 排序
            return sortAutoConfigurations(processedConfigurations, getAutoConfigurationMetadata()).stream()
                    .map((importClassName) -> new Entry(this.entries.get(importClassName), importClassName))
                    .collect(Collectors.toList());
        }

后续由 spring 进行不再继续跟踪

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文