Spring源码学习:@SpringBootApplication注解和自动装配原理学习

前言

我们在启动类使用该注解可以自动完成springboot项目的自动处理注解,自动导入各种依赖包等功能。那么追根溯源,这些是如何通过一个注解就完成的呢?下面我将结合源码学习探究底层的实现原理。
该篇文章涉及到对配置类的解析,需要提前了解ConfigurationClassPostProcessor是如何判定配置类,对配置类进行解析的,可以参考我写的另一篇文章
内置bean工厂后置处理器ConfigurationClassPostProcessor源码学习.

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

出去基本的四个元注解,该注解主要包括以下几个复合注解

  1. @SpringBootConfiguration
  2. @EnableAutoConfiguration
  3. @ComponentScan

这几个注解完成了@SpringBootApplication的主要功能

1.1 @SpringBootConfiguration

该注解定义如下
注解图
可以看出该注解就是springboot下的@Configuration注解,也就是该注解同@Configuration拥有同样的作用。被该注解修饰意味这该类是一个配置类,可以被ConfigurationClassPostProcessor后置处理器进行处理,从而完成对@Import,@ComponentScan和@Component等注解的处理和对应bd注册。

1.2 @EnableAutoConfiguration

该注解定义如下
注解定义
该注解也是个复合的注解,主要是引入了两个重要注解来实现自动配置

  • @AutoConfigurationPackage
  • @Import(AutoConfigurationImportSelector.class)

1.2.1 @AutoConfigurationPackage

该注解定义如下
注解信息
主要是通过@Import导入了AutoConfigurationPackages.Registrar这个类该类继承关系如下
继承关系
继承接口ImportBeanDefinitionRegistrar实现了注册bean的功能,DeterminableImports设置添加指定注解类所在的包为自动配置包。
也就是说添加了注解@AutoConfigurationPackage会将该类所在的package作为自动配置的package。

1.2.2 @Import(AutoConfigurationImportSelector.class)

主要是导入AutoConfigurationImportSelector这个类,该类继承关系如下
继承关系

  • 可以看出该类实现了各种Aware接口,可以通过invokeAwareMethods感知设置所需要的各种上下文属性。
  • 同时实现了ImportSelector接口,可以动态选择需要导入的配置类,直接实现的DeferredImportSelector是在配置类解析完之后再处理该类,进行自动导入等。
  • 实现Orderer接口可以对多个该类进行排序处理设置优先级。

1.3 @ComponentScan

实现该注解可以对basePackages进行扫描,将各种@Component解析注册为bd,定义如下
测试
启动类实现该注解,也就可以解析启动类所在包中各个子包中实现了指定组件注解的类。

2 源码解析

2.1 解析导入的@Import类

由于该注解包括了@SpringBootConfiguration,所以被修饰的启动类会被当做配置类处理。对该注解的处理最终会进入到ConfigurationClassParser.doProcessConfigurationClass,其实主要是对@Import的处理,

//处理@Import导入的类,getImports获取所有@Import导入的类包括注解中的@Import
this.processImports(configClass, sourceClass, this.getImports(sourceClass), filter, true);
        

进入该函数内部

private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
								Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter,
								boolean checkForCircularImports) {

		//没有@Import直接返回
		if (importCandidates.isEmpty()) {
			return;
		}
		/**执行循环引入检查
		 * 通过内置静态类ImportStack的imports判断是否存在循环导入,比如a导入b,b导入c,c导入a
		 * 出现循环导入问题直接抛出错误
		 */
		if (checkForCircularImports && isChainedImportOnStack(configClass)) {
			this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
		} else {
			this.importStack.push(configClass);
			try {
				for (SourceClass candidate : importCandidates) {
					//候选类是否是ImportSelector的子接口或者子类
					if (candidate.isAssignable(ImportSelector.class)) {
						// Candidate class is an ImportSelector -> delegate to it to determine imports
						Class<?> candidateClass = candidate.loadClass();
						ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,
								this.environment, this.resourceLoader, this.registry);
						Predicate<String> selectorFilter = selector.getExclusionFilter();
						if (selectorFilter != null) {
							exclusionFilter = exclusionFilter.or(selectorFilter);
						}
						if (selector instanceof DeferredImportSelector) {
							this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
						} else {
							String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
							Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
							processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
						}
					}
					//候选类是否是ImportBeanDefinitionRegistrar的子接口或者子类
					else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
						// Candidate class is an ImportBeanDefinitionRegistrar ->
						// delegate to it to register additional bean definitions
						Class<?> candidateClass = candidate.loadClass();
						ImportBeanDefinitionRegistrar registrar =
								ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
										this.environment, this.resourceLoader, this.registry);
						configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
					} else {
						// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
						// process it as an @Configuration class
						this.importStack.registerImport(
								currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
						//当作普通配置类处理
						processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
					}

2.1.1 @Import(AutoConfigurationPackages.Registrar.class)

由于其满足candidate.isAssignable(ImportBeanDefinitionRegistrar.class)进入该if语句,主要是加载该类Class,并实例化(ParserStrategyUtils.instantiateClass)
实例化
由于AutoConfigurationPackages.Registrar没有实现各种Aware接口,所以不会注入各种属性。

2.1.2 AutoConfigurationImportSelector

由于实现了ImportSelector和DeferredImportSelector对于处理如下

//进入该if语句
if (candidate.isAssignable(ImportSelector.class)) {
//加载类信息
                            candidateClass = candidate.loadClass();
                            实例化,并进行各种Aware注入
                            ImportSelector selector = (ImportSelector)ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class, this.environment, this.resourceLoader, this.registry);
                            Predicate<String> selectorFilter = selector.getExclusionFilter();
                            if (selectorFilter != null) {
                                exclusionFilter = exclusionFilter.or(selectorFilter);
                            }
							//进入该if语句
                            if (selector instanceof DeferredImportSelector) {
                                //并放入deferredImportSelectors,当所有配置类处理完,再处理该延迟导入选择类
                                this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector)selector);
                            } else {
                                String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
                                Collection<ConfigurationClassParser.SourceClass> importSourceClasses = this.asSourceClasses(importClassNames, exclusionFilter);
                                this.processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
                            }
                        

主要做了下面几个事:

  1. 加载,实例化该类,注入各种属性。
  2. 判断是否延迟处理,该类和配置类封装为DeferredImportSelectorHolder
  3. 加入到 ConfigurationClassParser.DeferredImportSelectorHandler内部类的缓存deferredImportSelectors中,后续进行延迟处理。

2.2 处理导入的@Import类

再处理完配置类,会回到ConfigurationClassParser.parse函数处理延迟导入选择器
this.deferredImportSelectorHandler.process(),处理逻辑

//获取对应的holder
List<ConfigurationClassParser.DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
            this.deferredImportSelectors = null;

            try {
                if (deferredImports != null) {
                //新建DeferredImportSelectorGroupingHandler
                    ConfigurationClassParser.DeferredImportSelectorGroupingHandler handler = ConfigurationClassParser.this.new DeferredImportSelectorGroupingHandler();
                    //排序
                    deferredImports.sort(ConfigurationClassParser.DEFERRED_IMPORT_COMPARATOR);
                    注册deferredImports到hanler
                    deferredImports.forEach(handler::register);
                    //真正处理逻辑
                    handler.processGroupImports();
                }

2.2.1 handler::register

public void register(ConfigurationClassParser.DeferredImportSelectorHolder deferredImport) {
			//获取DeferredImportSelector中的group
            Class<? extends Group> group = deferredImport.getImportSelector().getImportGroup();
            //创建DeferredImportSelectorGrouping,设置group属性
            ConfigurationClassParser.DeferredImportSelectorGrouping grouping = (ConfigurationClassParser.DeferredImportSelectorGrouping)this.groupings.computeIfAbsent(group != null ? group : deferredImport, (key) -> {
                return new ConfigurationClassParser.DeferredImportSelectorGrouping(this.createGroup(group));
            });
            //设置deferredImports属性
            grouping.add(deferredImport);
            //添加到缓存中configurationClasses
            this.configurationClasses.put(deferredImport.getConfigurationClass().getMetadata(), deferredImport.getConfigurationClass());
        }

该处理过程主要对DeferredImportSelectorGroupingHandler设置其中的属性值:groupingsconfigurationClasses,从而再2.2.2进行处理

2.2.2 handler.processGroupImports()

2.2.2.1 processGroupImports处理逻辑
Iterator var1 = this.groupings.values().iterator();
			//遍历处理groupings

            while(var1.hasNext()) {
            //获取grouping属性
                ConfigurationClassParser.DeferredImportSelectorGrouping grouping = (ConfigurationClassParser.DeferredImportSelectorGrouping)var1.next();
                //获取过滤器,后边对配置类进行处理,可以根据此进行过滤
                Predicate<String> exclusionFilter = grouping.getCandidateFilter();
                //获取自动导入的类,并放入缓存中
                grouping.getImports().forEach((entry) -> {
                    ConfigurationClass configurationClass = (ConfigurationClass)this.configurationClasses.get(entry.getMetadata());

                    try {
                    //解析刚才添加的自动导入的类主要是解析这些类导入的@Import
                        ConfigurationClassParser.this.processImports(configurationClass, ConfigurationClassParser.this.asSourceClass(configurationClass, exclusionFilter), Collections.singleton(ConfigurationClassParser.this.asSourceClass(entry.getImportClassName(), exclusionFilter)), exclusionFilter, false);
                    }
2.2.2.2 grouping.getImports处理逻辑

上述获取自动导入的类主要是grouping.getImports()
导入类
该方法主要是调用process获取,过滤自动导入类,并通过selectImports返回结果

2.2.2.3 group.process处理逻辑
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(annotationMetadata);
			//把自动配置类放到缓存中
			this.autoConfigurationEntries.add(autoConfigurationEntry);
			for (String importClassName : autoConfigurationEntry.getConfigurations()) {
				this.entries.putIfAbsent(importClassName, annotationMetadata);
			}
		}

该方法主要负责对获取自动配置类,并放到AutoConfigurationImportSelector.AutoConfigurationGroup.entries

2.2.2.4 AutoConfigurationImportSelector.getAutoConfigurationEntry处理逻辑
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
		//从系统环境查找是否存在环境属性"spring.boot.enableautoconfiguration",不存在返回默认值true,这里直接返回true,不进入该if语句
		if (!isEnabled(annotationMetadata)) {
			return EMPTY_ENTRY;
		}
		//获取注解属性,这里是EnableAutoConfiguration属性
		AnnotationAttributes attributes = getAttributes(annotationMetadata);
		//加载获取spring.factory中自动配置类
		List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
		//删除重复项
		configurations = removeDuplicates(configurations);
		//获取需要排除的类
		Set<String> exclusions = getExclusions(annotationMetadata, attributes);
		//检查排除类
		checkExcludedClasses(configurations, exclusions);
		//从候选类中去除需要排除的类
		configurations.removeAll(exclusions);
		//对候选类根据过滤条件进行过滤(主要是根据配置文件spring-autoconfigure-metadata.properties,都是自动配置类注解元数据),只加载需要的
		configurations = getConfigurationClassFilter().filter(configurations);
		//触发自动配置事件,调用监听器处理(如果实现了)
		fireAutoConfigurationImportEvents(configurations, exclusions);
		//封装返回最终的加载的类
		return new AutoConfigurationEntry(configurations, exclusions);
	}

该类主要是是加载spring.factories自动导入类,进行去重、根据spring-autoconfigure-metadata.properties进行过滤等。

2.2.2.5 AutoConfigurationImportSelector.getCandidateConfigurations处理逻辑
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;

主要是调用SpringFactoriesLoader.loadFactoryNames
加载
该方法获取类加载器,然后调用loadSpringFactories,该方法核心是加载配置文件

//是否已经加载过
Map<String, List<String>> result = (Map)cache.get(classLoader);
        if (result != null) {
            return result;
        } else {
            HashMap result = new HashMap();

            try {
            	//加载spring.factories,并进行解析放到cache缓存中
                Enumeration urls = classLoader.getResources("META-INF/spring.factories");

                while(urls.hasMoreElements()) {
                    URL url = (URL)urls.nextElement();
                    UrlResource resource = new UrlResource(url);
                    Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                    Iterator var6 = properties.entrySet().iterator();

                    while(var6.hasNext()) {
                        Entry<?, ?> entry = (Entry)var6.next();
                        String factoryTypeName = ((String)entry.getKey()).trim();
                        String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
                        String[] var10 = factoryImplementationNames;
                        int var11 = factoryImplementationNames.length;

                        for(int var12 = 0; var12 < var11; ++var12) {
                            String factoryImplementationName = var10[var12];
                            ((List)result.computeIfAbsent(factoryTypeName, (key) -> {
                                return new ArrayList();
                            })).add(factoryImplementationName.trim());
                        }
                    }
                }

                result.replaceAll((factoryType, implementations) -> {
                    return (List)implementations.stream().distinct().collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
                });
                cache.put(classLoader, result);
                return result;

该类主要就是加载spring.factories文件内容放到缓存cache中,方便后续直接缓存获取,不用每次都要磁盘加载。
getOrDefault方法对cache中内容根据org.springframework.boot.autoconfigure.EnableAutoConfiguration进行筛选,获取该名称下的所有自动配置类
加载的结果如下总共133个条目(2.6.4版本
在这里插入图片描述

2.2.2.6 getConfigurationClassFilter()处理逻辑

该方法主要是获取spring.factories中配置的自动配置过滤,如下图

过滤器
主要包括三个过滤器:

  1. org.springframework.boot.autoconfigure.condition.OnBeanCondition
  2. org.springframework.boot.autoconfigure.condition.OnClassCondition
  3. org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition

该方法核心逻辑如下

if (this.configurationClassFilter == null) {
			/**
			** 该方法同上文加载spring.factory文件类似,由于此时缓存cache已经有值
			** 所有直接获取org.springframework.boot.autoconfigure.AutoConfigurationImportFilter对应的值,并进行了实例化
			**/
			List<AutoConfigurationImportFilter> filters = getAutoConfigurationImportFilters();
			//实现对各种Aware的感知和设置
			for (AutoConfigurationImportFilter filter : filters) {
				invokeAwareMethods(filter);
			}
			this.configurationClassFilter = new ConfigurationClassFilter(this.beanClassLoader, filters);
		}
		return this.configurationClassFilter;
	}

过滤逻辑

String[] candidates = StringUtils.toStringArray(configurations);
			//设置标志位是否跳过,默认false
			boolean skipped = false;
			for (AutoConfigurationImportFilter filter : this.filters) {
				boolean[] match = filter.match(candidates, this.autoConfigurationMetadata);
				for (int i = 0; i < match.length; i++) {
					//false,需要跳过,赋值null,设置标志位为true
					if (!match[i]) {
						candidates[i] = null;
						skipped = true;
					}
				}
			}
			//没有要跳过的,直接返回
			if (!skipped) {
				return configurations;
			}
			//将跳过的忽略,剩余的放到result,返回result
			List<String> result = new ArrayList<>(candidates.length);
			for (String candidate : candidates) {
				if (candidate != null) {
					result.add(candidate);
				}
			}

过滤原理

  1. OnClassCondition:判断是否pom导入了对应的类,如果没有引入jar包,直接过滤掉。
  2. OnBeanCondition:判断当前依赖的bean,是否存在对应的实例对象,存在才满足。
  3. OnWebApplicationCondition:当前处于web环境时,才会生效,否则过滤掉。

再上述进行过滤后剩余结果如下
剩余类
后续我们就可以对这些结果注册到bd中,进行正常的实例化,初始化等设置。

3 处理流程图

针对@SpringBootApplication注解,从启动类的调用过程到解析为bd的主要流程处理如下,不包括后续的实例化过程,和@Import导入类的注册bd过程,具体如下图
注解流程图

总结

本文主要从@SpringBootApplication注解的组成,源码的解析过程和处理的流程图做了一个学习记录,部分内容没有详细进行介绍,如有迷惑之处,可以参考源码进行学习。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

LamaxiyaFc

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值