SpringBoot自动装配原理分析之中篇

1. @SpringBootApplication

1.1 @SpringBootApplication注解

万事开头难,一旦开始就不要停,不要停,不要,直到她说停。
一切从 @SpringBootApplication 注解开始说起,它是一个组合注解,我们先看看源码怎么写的。

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

}

@Inherited注解声明注解上时,在类上表明该类具有继承性。子类会自动继承该注解。
需要我们注重关注三个注解:@SpringBootConfiguration@ComponentScan@EnableAutoConfiguration

1.1.1 @SpringBootConfiguration
1.1.2 @ComponentScan
1.1.3 @EnableAutoConfiguration
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

    Class<?>[] exclude() default {};

    String[] excludeName() default {};
}

@EnableAutoConfiguration 注解上使用了 @Import注解导入了一个配置类AutoConfigurationImportSelector,看到这里如果看过前一篇
SpringBoot自动装配原理分析之上篇我想你应该知道AutoConfigurationImportSelector必然是ImportSelectorImportBeanDefinitionRegistrar的实现类。

2 AutoConfigurationImportSelector

我们可以看到一些信息:

  • AutoConfigurationImportSelector它实现了DeferredImportSelector接口,而DeferredImportSelector接口继承自ImportSelector接口;
  • 实现了Ordered接口定义加载顺序的接口;
//从定义上可以看到实现了DeferredImportSelector接口
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
	//....略
}

2.1 解读方法#selectImports

小伙伴儿,我们回到AutoConfigurationImportSelector类中,又要准备深入了,鸡冻不…哈哈
看看方法名,selectImports选择导入,导入什么,显然就是要自动装配的类。怎么还要选择性的导入自动装配呢?我们想想是不是要过滤一些,排除一些多余的呢?这个猜想不错,我们看看就明白究竟了。

//入参是注解属性元信息
public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        } else {
            AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
            AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
            //获取自动装配候选名单
            List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
            //去除重复
            configurations = this.removeDuplicates(configurations);
            //去除自动装配排除的名单列表
            Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
            //检查排除类名单是否合法
            this.checkExcludedClasses(configurations, exclusions);
            //从候选名单列表中移除需要排除的名单列表
            configurations.removeAll(exclusions);
            //过滤自动装配组件
            configurations = this.filter(configurations, autoConfigurationMetadata);
            //自动装配事件
            this.fireAutoConfigurationImportEvents(configurations, exclusions);
            return StringUtils.toStringArray(configurations);
        }
    }

小伙伴儿们,又需要我们逐一层层深入了。记住,只有深入了,你才会体验到前所未有的爽,那感觉终生难忘,回味无穷,嘿嘿,,,,看源码是痛苦的,但看明白后就有种醍醐灌顶,豁然开朗的感觉。

2.2 获取候选装配组件

解读方法getCandidateConfigurations从命名上可以知道Candidate是候选的意思,大概就是获取候选配置类信息

public class AutoConfigurationImportSelector{
 protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
 //加载在 `META-INF/spring.factories` 里的类名的数组,他们作为自动装配的候选类名单
        List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.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;
    }
	}
}

我们继续跟进SpringFactoriesLoader.loadFactoryNames这个方法看看

public final class SpringFactoriesLoader {
	//配置文件路径
    public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
 	public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
        String factoryTypeName = factoryType.getName();
        //此处又调用了该类的私有方法#loadSpringFactories()
        return (List)loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
    }
    
//小伙伴儿们我们继续深入....有多深都要进去,只是进去看看
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
		//MultiValueMap就是个Map,因为返回就是Map
        MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
		//加载指定路径下文件内容
		Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
		LinkedMultiValueMap result = new LinkedMultiValueMap();

		while(urls.hasMoreElements()) {
			URL url = (URL)urls.nextElement();
			//转为Resource
			UrlResource resource = new UrlResource(url);
			//加载为Peroperties文件进行读取
			Properties properties = PropertiesLoaderUtils.loadProperties(resource);
			Iterator var6 = properties.entrySet().iterator();

			while(var6.hasNext()) {
				Entry<?, ?> entry = (Entry)var6.next();
				//我们打开META-INF/spring.factories文件看看,
				//可以发现文件key是接口的全类名,value是实现类的全类名列表,代码自然就懂了
				String factoryTypeName = ((String)entry.getKey()).trim();
				String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
				int var10 = var9.length;
				for(int var11 = 0; var11 < var10; ++var11) {
					String factoryImplementationName = var9[var11];
					result.add(factoryTypeName, factoryImplementationName.trim());
				}
			}
		}

		cache.put(classLoader, result);
		return result;         
    }

}

说明SpringFactoriesLoader是Spring的工厂机制加载器,看完#loadSpringFactories方法,明白原理如下:

  • 加载指定ClassLoader下META-INF/spring.factories文件资源内容
  • 转为Properties文件进行读取,放入Map中,key为接口的全类名,value为实现类的全类名列表,然后进行返回。

找到maven依赖的自动装配模块org.springframework.boot:spring-boot-autoconfigure,META-INF/spring.factories文件下,以EnableAutoConfiguration全类名为Key的配置如下所示:

# 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,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.cloud.CloudAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\
//其他略.......

2.3 排除自动装配组件

接下来,执行this.getExclusions(annotationMetadata, attributes) 方法,去排除一些不需要的组件。
这样说你可能还不清楚,比如在@EnableAutoConfiguration注解定义中可以看到

public @interface SpringBootApplication{
    Class<?>[] exclude() default {};
    String[] excludeName() default {};
}

有两个属性分别是excludeexcludeName,在实际项目中你可能也这样写过,配置多数据源时,需要我们排除默认的数据源,使用我们自己配置的数据源,配置如下:

@SpringBootApplication(exclude={
	DataSourceAutoConfiguration.class,
	DataSourceTransactionManagerAutoConfiguration.class})

这就是要告诉自动装配需要排除的组件类了。

protected Set<String> getExclusions(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        Set<String> excluded = new LinkedHashSet();
        //@EnableAutoConfiguration注解中exclude和excludeName用来添加排除的组件
        excluded.addAll(this.asList(attributes, "exclude"));
        excluded.addAll(Arrays.asList(attributes.getStringArray("excludeName")));
        excluded.addAll(this.getExcludeAutoConfigurationsProperty());
        return excluded;
    }

将需要排除的类放在集合中,然后返回,之后检查排除的集合类是否合法最后进行移除。

2.4 过滤自动装配组件

继续往下看了,调用configurations = this.filter(configurations, autoConfigurationMetadata); 方法进行过滤。

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;
        //主要方法getAutoConfigurationImportFilters()
        Iterator var8 = this.getAutoConfigurationImportFilters().iterator();

        while(var8.hasNext()) {
        //可以看出迭代的类是AutoConfigurationImportFilter的子类!!!!
            AutoConfigurationImportFilter filter = (AutoConfigurationImportFilter)var8.next();
            this.invokeAwareMethods(filter);
            boolean[] match = filter.match(candidates, autoConfigurationMetadata);
		//为节省篇幅,其他代码省略....
            
        }

        
    }
    
//#filter()方法调用
protected List<AutoConfigurationImportFilter> getAutoConfigurationImportFilters() {
        return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class, this.beanClassLoader);
    }
    

返回进入SpringFactoriesLoader

public static <T> List<T> loadFactories(Class<T> factoryClass, @Nullable ClassLoader classLoader) {
		Assert.notNull(factoryClass, "'factoryClass' must not be null");
		ClassLoader classLoaderToUse = classLoader;
		if (classLoaderToUse == null) {
			classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
		}
		List<String> factoryNames = loadFactoryNames(factoryClass, classLoaderToUse);
		
		return result;
	}

可以看出AutoConfigurationImportSelector#filter中的AutoConfigurationImportFilter 对象集合是有SpringFactoriesLoader 加载的。找到maven依赖的自动装配模块org.springframework.boot:spring-boot-autoconfigure,META-INF/spring.factories文件下

# Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnClassCondition

我们可以看到,SpringFactoriesLoader#loadFactories方法与我们前面看获取候选自动装配方法时调用的SpringFactoriesLoader#loadFactoryNames 差别是后者是静态方法,前者是调用后者的。点开OnClassCondition 可以发现它是AutoConfigurationImportFilter实现类。可以说明AutoConfigurationImportSelector#filter方法的作用是过滤META-INF/spring.factories 资源中那些当前ClassLoader不存在的类。
SpringBoot内部提供了特有的注解:条件注解(Conditional Annotation)。比如@ConditionalOnBean、@ConditionalOnClass、@ConditionalOnExpression、@ConditionalOnMissingBean等。@ConditionalOnClass用来判断需要依赖自动装配的class必需被ClassLoader提前装载,然后解析其注解元信息,从而跟据依赖是否存在来判断是否装载。

2.4 自动装配事件
private void fireAutoConfigurationImportEvents(List<String> configurations, Set<String> exclusions) {
        List<AutoConfigurationImportListener> listeners = this.getAutoConfigurationImportListeners();
        if (!listeners.isEmpty()) {
            AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this, configurations, exclusions);
            Iterator var5 = listeners.iterator();

            while(var5.hasNext()) {
                AutoConfigurationImportListener listener = (AutoConfigurationImportListener)var5.next();
                this.invokeAwareMethods(listener);
                listener.onAutoConfigurationImportEvent(event);
            }
        }
    }

AutoConfigurationImportListener用来监听AutoConfigurationImportEvent

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值