java -springboot 02 自动装配 @SpringBootApplication

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 {

从上述源码可以看出,@SpringBootApplication注解是一个组合注解,前面 4 个是注解的元数据信 息, 我们主要看后面 3 个注解:@SpringBootConfiguration、@EnableAutoConfiguration、 @ComponentScan三个核心注解,关于这三个核心注解的相关说明具体如下

2.@SpringBootConfiguration:
SpringBoot 的配置类,标注在某个类上,表示这是一个 SpringBoot 的配置类。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration // 配置类的作用等同于配置文件,配置类也是容器中的一个对象
public @interface SpringBootConfiguration {
}

从上述源码可以看出,@SpringBootConfiguration注解内部有一个核心注解@Configuration,该注解 是Spring框架提供的,表示当前类为一个配置类(XML配置文件的注解表现形式),并可以被组件扫描 器扫描。由此可见,@SpringBootConfiguration注解的作用与@Configuration注解相同,都是标识一 个可以被组件扫描器扫描的配置类,只不过@SpringBootConfiguration是被Spring Boot进行了重新封 装命名而已

3.@EnableAutoConfiguration :(重点)  自动配置注解,此注解表示开启自动配置功能

Spring 中有很多以 Enable 开头的注解,其作用就是借助 @Import 来收集并注册特定场景相关的 Bean ,并加载到 IOC 容器。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited

@AutoConfigurationPackage // 自动配置包
@Import(AutoConfigurationImportSelector.class) // Spring的底层注解@Import,给容器中导入一个组件;
public @interface EnableAutoConfiguration {

这也是个组合注解,主要关注@AutoConfigurationPackage @Import(AutoConfigurationImportSelector.class)

3.1.@AutoConfigurationPackage:
Spring的底层注解@Import,给容器中导入一个组件,导入的组件AutoConfigurationPackages.Registrar.class

	// Spring的底层注解@Import,给容器中导入一个组件;
	// 导入的组件是AutoConfigurationPackages.Registrar.class
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
 
}

3.1.1@Import(AutoConfigurationPackages.Registrar.class) :

它就是将 Registrar 这个组件类导入 到容器中,可查看 Registrar 类中 registerBeanDefinitions 方法:

static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
 
		@Override
		public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
			// 将注解标注的元信息传入,获取到相应的包名
			register(registry, new PackageImport(metadata).getPackageName());
		}
 
		@Override
		public Set<Object> determineImports(AnnotationMetadata metadata) {
			return Collections.singleton(new PackageImport(metadata));
		}
 
	}

3.1.1.1 register方法

new PackageImport(metadata).getPackageName())这个语句:
在这里插入图片描述

public static void register(BeanDefinitionRegistry registry, String... packageNames) {
		// 这里参数 packageNames 缺省情况下就是一个字符串,是使用了注解
		// @SpringBootApplication 的 Spring Boot 应用程序入口类所在的包
 
		if (registry.containsBeanDefinition(BEAN)) {
			// 如果该bean已经注册,则将要注册包名称添加进去
			BeanDefinition beanDefinition = registry.getBeanDefinition(BEAN);
			ConstructorArgumentValues constructorArguments = beanDefinition.getConstructorArgumentValues();
			constructorArguments.addIndexedArgumentValue(0, addBasePackages(constructorArguments, packageNames));
		}
		else {
			//如果该bean尚未注册,则注册该bean,参数中提供的包名称会被设置到bean定义中去
			GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
			beanDefinition.setBeanClass(BasePackages.class);
			beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, packageNames);
			beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
			registry.registerBeanDefinition(BEAN, beanDefinition);
		}
	}

3.2.@Import(AutoConfigurationImportSelector.class)

1.给容器导入AutoConfigurationImportSelector组件, AutoConfigurationImportSelector 可以帮助 Springboot 应用
将所有符合条件的 @Configuration 配置都加载到当前 SpringBoot 创建并使用的 IOC 容器( ApplicationContext )中。 ·
2.启动时,会执行该方法来进行自动配置,低版本springboot通过selectImports方法来进行配置

在这里插入图片描述

可以看到 AutoConfigurationImportSelector 重点是实现了 DeferredImportSelector 接口和各种 Aware 接口,然后 DeferredImportSelector 接口又继承了 ImportSelector 接口。 其不光实现了 ImportSelector 接口,还实现了很多其它的 Aware 接口,分别表示在某个时机会被回 调。

3.2.1

自动配置逻辑相关的入口方法在 DeferredImportSelectorGrouping 类的 getImports 方法处, 因此我们就从 DeferredImportSelectorGrouping 类的 getImports 方法来开始分析SpringBoot的自动配置源码

public Iterable<Group.Entry> getImports() {
// 遍历DeferredImportSelectorHolder对象集合deferredImports,deferredImports集合装了各种ImportSelector,当然这里装的是AutoConfigurationImportSelector
for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
// 【1】,利用AutoConfigurationGroup的process方法来处理自动配置的相关逻辑,决定导入哪些配置类(这个是我们分析的重点,自动配置的逻辑全在这了)
this.group.process(deferredImport.getConfigurationClass().getMetadata(),deferredImport.getImportSelector());
}
// 【2】,经过上面的处理后,然后再进行选择导入哪些配置类
return this.group.selectImports();
}

标 【1】 处的的代码是我们分析的重中之重,自动配置的相关的绝大部分逻辑全在这里了。那么 this.group.process(deferredImport.getConfigurationClass().getMetadata(),deferredImport.getImportSelector()) ;主要做的事情就是在 this.group 即 AutoConfigurationGroup 对象的 process 方法中,传入的 AutoConfigurationImportSelector 对象来选择一些符合条件的自动配置类,过滤掉一些不符合条件的自动配置类,就是这么个事情。

3.2.1.1

process 方法:

	// 这里用来处理自动配置类,比如过滤掉不符合匹配条件的自动配置类
		@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()));
 
			// 【1】,调用getAutoConfigurationEntry方法得到自动配置类放入autoConfigurationEntry对象中
			AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
					.getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata);
 
			// 【2】,又将封装了自动配置类的autoConfigurationEntry对象装进autoConfigurationEntries集合
			this.autoConfigurationEntries.add(autoConfigurationEntry);
			// 【3】,遍历刚获取的自动配置类
			for (String importClassName : autoConfigurationEntry.getConfigurations()) {
				// 这里符合条件的自动配置类作为key,annotationMetadata作为值放进entries集合
				this.entries.putIfAbsent(importClassName, annotationMetadata);
			}
		}

3.2.1.1.1 getAutoConfigurationEntry方法:这个方法主要是用来获取自动 配置类有关,承担了自动配置的主要逻辑。

	protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return EMPTY_ENTRY;
		}
		// 获取@EnableAutoConfiguration的属性
		AnnotationAttributes attributes = getAttributes(annotationMetadata);
		// 获取spring.factories中所有的AutoConfiguration
		List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
		// 去重(也就是按类名去重)
		configurations = removeDuplicates(configurations);
		// 获取需要排除的AutoConfiguration,可以通过@EnableAutoConfiguration注解的exclude属性,或者spring.autoconfigure.exclude来配置
		Set<String> exclusions = getExclusions(annotationMetadata, attributes);

		// 排除
		checkExcludedClasses(configurations, exclusions);
		configurations.removeAll(exclusions);

		// 获取spring.factories中的AutoConfigurationImportFilter对AutoConfiguration进行过滤
		// 默认会拿到OnBeanCondition、OnClassCondition、OnWebApplicationCondition
		// 这三个会去判断上面的AutoConfiguration是否符合它们自身所要求的条件,不符合的会过滤掉,表示不会进行解析了
		// 会利用spring-autoconfigure-metadata.properties中的配置来进行过滤
		// spring-autoconfigure-metadata.properties文件中的内容是利用Java中的AbstractProcessor技术在编译时生成出来的
		configurations = getConfigurationClassFilter().filter(configurations);
		// configurations表示合格的,exclusions表示被排除的,把它们记录在ConditionEvaluationReportAutoConfigurationImportListener中
		fireAutoConfigurationImportEvents(configurations, exclusions);

		// 最后返回的AutoConfiguration都是符合条件的
		return new AutoConfigurationEntry(configurations, exclusions);
	}

3.2.1.1.1.1 getCandidateConfigurations方法:getCandidateConfigurations这个方法中有一个重要方法 loadFactoryNames ,这个方法是让 SpringFactoryLoader 去加载一些组 件的名字。

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
		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;
	}

3.2.1.1.1.1.1 继续点开 loadFactoryNames方法:

	public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable
			ClassLoader classLoader) {
//获取出入的键
		String factoryClassName = factoryClass.getName();
		return
				(List)loadSpringFactories(classLoader).getOrDefault(factoryClassName,
						Collections.emptyList());
	}
	private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
		MultiValueMap<String, String> result =
				(MultiValueMap)cache.get(classLoader);
		if (result != null) {
			return result;
		} else {
			try {
//如果类加载器不为null,则加载类路径下spring.factories文件,将其中设置的配置类的全路径信息封装 为Enumeration类对象
				Enumeration<URL> urls = classLoader != null ?
						classLoader.getResources("META-INF/spring.factories") :
						ClassLoader.getSystemResources("META-INF/spring.factories");
				LinkedMultiValueMap result = new LinkedMultiValueMap();
//循环Enumeration类对象,根据相应的节点信息生成Properties对象,通过传入的键获取值,在将值切割为一个个小的字符串转化为Array,方法result集合中
				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 factoryClassName =
								((String)entry.getKey()).trim();
						String[] var9 =
								StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
						int var10 = var9.length;
						for(int var11 = 0; var11 < var10; ++var11) {
							String factoryName = var9[var11];
							result.add(factoryClassName, factoryName.trim());
						}
					}
				}
				cache.put(classLoader, result);
				return result;
			}
		}
	}

从代码中我们可以知道,在这个方法中会遍历整个ClassLoader中所有jar包下的spring.factories文件。 spring.factories里面保存着springboot的默认提供的自动配置类。在这里插入图片描述
getAutoConfigurationEntry 方法主要做的事情:
【1】从 spring.factories 配置文件中加载 EnableAutoConfiguration 自动配置类),获取的自动配 置类如图所示。

【2】若 @EnableAutoConfiguration 等注解标有要 exclude 的自动配置类,那么再将这个自动配置类 排除掉;

【3】排除掉要 exclude 的自动配置类后,然后再调用 filter 方法进行进一步的过滤,再次排除一些 不符合条件的自动配置类;

【4】经过重重过滤后,此时再触发 AutoConfigurationImportEvent 事件,告诉 ConditionEvaluationReport 条件评估报告器对象来记录符合条件的自动配置类;

【5】 最后再将符合条件的自动配置类返回。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值