SpringBoot自动装配原理分析一:AutoConfigurationImportSelector类的解析过程

SpringBoot自动装配原理分析:AutoConfigurationImportSelector

1、注解@SpringBootApplication

进入启动类源码可以看到@SpringBootApplication注解是一个组合注解,他其实是由@SpringBootConfiguration

@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、@SpringBootConfiguration

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {}

可以看到其实是一个被@Configuration标记的配置类,与@Controller、@Service一样,只不过区分了使用场景,本质上就是@Component,标记为一个启动类。

1.2、@ComponentScan

spring的注解,用来开启注解扫描,其实就是限定扫描哪写被@Component注解的类。

1.3、@EnableAutoConfiguration

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

可以看到两个比较重要的地方@AutoConfigurationPackage以及@Import(AutoConfigurationImportSelector.class),主要的在AutoConfigurationImportSelector 类中,它实现了DeferredImportSelector,而DeferredImportSelector又实现了ImportSelector,所以是ImportSelector的一个实现类。

2、分析AutoConfigurationImportSelector

AutoConfigurationImportSelector实现了ImportSelector的方法selectImports,之前会执行该方法,不过自从引入了DeferredImportSelector,实现ImportSelectorselectImports再也没有执行了,而是转换成了由内部类AutoConfigurationGroup的实现了DeferredImportSelector的内部类Group的两个方法process和selectImports作为入口(有点绕。。。。。)

2.1、分析process方法

@Override
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
    1、获取所有的自动配置
        getAutoConfigurationEntry():从META-INF/spring.factories获取
        getAutoConfigurationMetadata():从META-INF/spring-autoconfigure-metadata.properties获取
        AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
        .
        getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata);
    this.autoConfigurationEntries.add(autoConfigurationEntry);
    for (String importClassName : autoConfigurationEntry.getConfigurations()) {
        this.entries.putIfAbsent(importClassName, annotationMetadata);
    }
}

这个方法重要的只有中间的getAutoConfigurationEntry调用,获取所有的自动配置类全限定类名;以及getAutoConfigurationMetadata方法,该方法获取了所有的每个配置类要创建的前提条件,也就是@Condition

2.2、getAutoConfigurationMetadata方法

private AutoConfigurationMetadata getAutoConfigurationMetadata() {
    if (this.autoConfigurationMetadata == null) {
        this.autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
    }
    return this.autoConfigurationMetadata;
}

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

static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader) {
    return loadMetadata(classLoader, PATH);
}

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);
    }
}

META-INF/spring-autoconfigure-metadata.properties该文件已经缓存了要创建配置类的前提条件,后续检查是否需要创建该配置类就是从该文件中去获取的。

举例:

org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration.ConditionalOnClass=javax.servlet.ServletRequest

意思是说:ServletWebServerFactoryAutoConfiguration类上的@ConditionalOnClass注解的条件是javax.servlet.ServletRequest

2.3、重点:getAutoConfigurationEntry方法

重点重点重点!重要的事情说三遍!

protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
                                                           AnnotationMetadata annotationMetadata) {
    1、查看是否开启了自动配置,默认是true
        if (!isEnabled(annotationMetadata)) {
            return EMPTY_ENTRY;
        }
    2、获取注解属性,也就是@EnableAutoConfiguration内的两个exclude和excludeName属性
        AnnotationAttributes attributes = getAttributes(annotationMetadata);
    3、从META-INF/spring.factories获取所有的候选配置
        List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
    4、移除重复的,就是用Set不可重复性质。
        configurations = removeDuplicates(configurations);
    5、获取排除的配置
        Set<String> exclusions = getExclusions(annotationMetadata, attributes);
    6、检验配置,其实就是排除的配置项在类加载器上但是不在候选者里面则抛出异常
        checkExcludedClasses(configurations, exclusions);
    7、删除排除的配置项
        configurations.removeAll(exclusions);
    8、过滤,这儿过滤一般就是@Condintion注解进行过滤,单独讲
        configurations = filter(configurations, autoConfigurationMetadata);
    9、事件发布
        fireAutoConfigurationImportEvents(configurations, exclusions);
    return new AutoConfigurationEntry(configurations, exclusions);
}

大流程来说还是比较简单的,较难的是里面的细节问题,需要分析@Condintion是如何过滤的。

2.4、getCandidateConfigurations方法

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
                                                                         getBeanClassLoader());
    return configurations;
}

public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
    1、就是注解类名称,org.springframework.boot.autoconfigure.EnableAutoConfiguration
        String factoryTypeName = factoryType.getName();
    return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
    1、先从缓存中获取与,spring的一贯做法,不在解释
        MultiValueMap<String, String> result = cache.get(classLoader);
    if (result != null) {
        return result;
    }

    try {
        2、FACTORIES_RESOURCE_LOCATION的值是:META-INF/spring.factories
            Enumeration<URL> urls = (classLoader != null ?
                                     classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
                                     ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
        result = new LinkedMultiValueMap<>();
        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();
                for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
                    result.add(factoryTypeName, factoryImplementationName.trim());
                }
            }
        }
        3、放到缓存当中,下次就不需要再次解析了。
            cache.put(classLoader, result);
        return result;
    }
}

该方法作用就是读取所有的候选配置,也就是从META-INF/spring.factories中读取所有的配置,最后封装成一个Map返回。

2.4、总结

  • 首先通过getAutoConfigurationMetadata()方法读取spring-autoconfigure-metadata.properties获得创建配置类的前提条件,封装到AutoConfigurationMetadataLoader的properties属性上。
  • 着手读取spring.factories文件获取自动装配类集合。
  • 读取@EnableAutoConfiguration的属性,exclude和excludeName。
  • 去重,检查自动装配的Class排除的集合是否合法。
  • 移除掉所有的exclude和excludeName中的名单
  • 再次过滤通过@Condintioni条件进行过滤。

3、过滤自动配置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;
    1、获取自动配置过滤器
        for (AutoConfigurationImportFilter filter : getAutoConfigurationImportFilters()) {
            invokeAwareMethods(filter);
            2、判断是否满足条件,否则后续需要移除掉。
                boolean[] match = filter.match(candidates, autoConfigurationMetadata);
            for (int i = 0; i < match.length; i++) {
                3、为false则需要移除,代表不满足条件
                    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++) {
        3、只保留skip为false的,返回过滤后的配置类
            if (!skip[i]) {
                result.add(candidates[i]);
            }
    }
    return new ArrayList<>(result);
}

3.1、getAutoConfigurationImportFilters方法

AutoConfigurationImportFilter同样也在META-INF/spring.factories下面,如下所示

org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnBeanCondition,\
org.springframework.boot.autoconfigure.condition.OnClassCondition,\
org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition

最后会返回这三个condition的类。

protected List<AutoConfigurationImportFilter> getAutoConfigurationImportFilters() {
    return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class, this.beanClassLoader);
}


public static <T> List<T> loadFactories(Class<T> factoryType, @Nullable ClassLoader classLoader) {
    ClassLoader classLoaderToUse = classLoader;
    if (classLoaderToUse == null) {
        classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
    }
    1、与前面获取配置文件内容类似,就是读取配置文件获得全限定类名
        List<String> factoryImplementationNames = loadFactoryNames(factoryType, classLoaderToUse);
    List<T> result = new ArrayList<>(factoryImplementationNames.size());
    for (String factoryImplementationName : factoryImplementationNames) {
        2、通过反射进行实例化对象
            result.add(instantiateFactory(factoryImplementationName, factoryType, classLoaderToUse));
    }
    AnnotationAwareOrderComparator.sort(result);
    return result;
}

3.2、match方法解析

OnClassCondition为例,他继承了FilteringSpringBootCondition。继承关系如下所示:
在这里插入图片描述

首先进入父类FilteringSpringBootCondition的match方法:

@Override
public boolean[] match(String[] autoConfigurationClasses, AutoConfigurationMetadata autoConfigurationMetadata) {
    ConditionEvaluationReport report = ConditionEvaluationReport.find(this.beanFactory);
    1ConditionOutcome中属性match为ture就保留,反之移除。
        ConditionOutcome[] outcomes = getOutcomes(autoConfigurationClasses, autoConfigurationMetadata);
    boolean[] match = new boolean[outcomes.length];
    for (int i = 0; i < outcomes.length; i++) {
        2、如果outcome为null代表该配置类上没有@OnClassCondition注解
            若存在则看match是否为true,满足这两个条件任意一个就保留。
            match[i] = (outcomes[i] == null || outcomes[i].isMatch());
        if (!match[i] && outcomes[i] != null) {
            3、事件发布
                logOutcome(autoConfigurationClasses[i], outcomes[i]);
            if (report != null) {
                report.recordConditionEvaluation(autoConfigurationClasses[i], this, outcomes[i]);
            }
        }
    }
    return match;
}

getOutcomes方法返回了一个包装类,若不为null,则根据内部包装类的match属性判断是否满足条件,若为null则说明该配置类上没有@Condition注解。

3.3、getOutcomes方法解析

@Override
protected final ConditionOutcome[] getOutcomes(String[] autoConfigurationClasses,
                                               AutoConfigurationMetadata autoConfigurationMetadata) {
    1、看cpu核心数量,是否满足多线程条件,多开一个线程解析。
        if (Runtime.getRuntime().availableProcessors() > 1) {
            2、将配置类数量分成两个数组,由两个线程解析最终还是调用了resolveOutcomes方法
                return resolveOutcomesThreaded(autoConfigurationClasses, autoConfigurationMetadata);
        }
    else {
        2、否则单线程解析
            OutcomesResolver outcomesResolver = new StandardOutcomesResolver(autoConfigurationClasses, 0,
                                                                             autoConfigurationClasses.length, autoConfigurationMetadata, getBeanClassLoader());
        return outcomesResolver.resolveOutcomes();
    }
}

3.4、resolveOutcomes方法

public ConditionOutcome[] resolveOutcomes() {
    return getOutcomes(this.autoConfigurationClasses, this.start, this.end, this.autoConfigurationMetadata);
}

private ConditionOutcome[] getOutcomes(String[] autoConfigurationClasses, int start, int end,
                                       AutoConfigurationMetadata autoConfigurationMetadata) {
    ConditionOutcome[] outcomes = new ConditionOutcome[end - start];
    1、遍历从spring.factory文件中读取的配置类
        for (int i = start; i < end; i++) {
            String autoConfigurationClass = autoConfigurationClasses[i];
            if (autoConfigurationClass != null) {
                2、获取当前的要创建类的前提条件
                    String candidates = autoConfigurationMetadata.get(autoConfigurationClass, "ConditionalOnClass");
                if (candidates != null) {
                    3、最终匹配,查看该类是否存在
                        outcomes[i - start] = getOutcome(candidates);
                }
            }
        }
    return outcomes;
}


private ConditionOutcome getOutcome(String candidates) {
    try {
        1、只有前提一个条件
            if (!candidates.contains(",")) {
                return getOutcome(candidates, this.beanClassLoader);
            }
        2、有多个前提条件,只要是一个不满足就返回
            for (String candidate : StringUtils.commaDelimitedListToStringArray(candidates)) {
                3、该方法若outcome不为null,返回的一定是不满足,为null说明该配置类没有条件或者条件满足
                    ConditionOutcome outcome = getOutcome(candidate, this.beanClassLoader);
                if (outcome != null) {
                    return outcome;
                }
            }
    }
    return null;
}


private ConditionOutcome getOutcome(String className, ClassLoader classLoader) {
    1、若返回true说明条件类不存在,将match设置为false
        若返回false说明创建条件类成功
        if (ClassNameFilter.MISSING.matches(className, classLoader)) {
            return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnClass.class)
                                            .didNotFind("required class").items(Style.QUOTE, className));
        }
    2、返回true说明条件类存在,或者配置类上没有@Condintion条件信息
        return null;
}



@Override
public boolean matches(String className, ClassLoader classLoader) {
    return !isPresent(className, classLoader);
}

static boolean isPresent(String className, ClassLoader classLoader) {
    if (classLoader == null) {
        classLoader = ClassUtils.getDefaultClassLoader();
    }
    try {
        1、真正判断条件的地方。实例化@CondintionOnClass指向的类,若类存在则通过反射创建对象,否则抛出异常,返回falseresolve(className, classLoader);
        return true;
    }
    catch (Throwable ex) {
        return false;
    }
}

该方法就是去真正的对比是否满足条件,具体的在isPresent方法处,通过实例化condintion来判断是否满足配置类创建的前提条件,若实例化成功则满足条件,否则配置类将被过滤

3.5、总结

filter方法的大致流程为:

  • 获得AutoConfigurationImportFilter,该类被OnBeanCondition、OnclassCondition、OnWebApplicationCondition三个类实现,进而转化为三各类各自的解析,进入match方法

  • 通过预先已经在META-INF/spring-autoconfigure-metadata.properties文件中设置好的配置类的前提条件,若条件为null,则说明该配置类不需要前提条件,反之获得Condition的全限定类名,然后通过反射机制创建实例,观察是否能创建成功,若创建成功说明满足配置类的创建条件,否则配置类将被过滤。具体的代码在这儿

    match[i] = (outcomes[i] == null || outcomes[i].isMatch()); outcomes为null或者isMatch为true,说明配置类无需前提条件或条件满足。

4、总结

总的来说AutoConfigurationImportSelector的读取自动配置的流程为:

  1. 首先从spring-autoconfigure-metadata.properties读取创建配置类的前提条件。
  2. spring.factories文件中读取所有自动配置类,封装成集合。
  3. 读取@EnableAutoConfiguration的属性也就是exclude和excludeName
  4. 校验候选者是否合法,也就是在排除集合中但是不在自动配置候选者中的抛出异常。
  5. 从候选者中剔除排除的集合。
  6. 再次过滤,通过@Condintion判断前提条件是否满足。
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值