SpringBoot自动装配原理以及starter技术(下)

        上一篇对Spring中@Import注解从常规使用和底层源码角度完成分析
SpringBoot自动装配原理以及starter技术(上),本节将在@Import注解基础上分析自动装配原理,本节安排如下:

  • @SpringBootApplication
  • DeferredImportSelectorHandler#handle
  • DeferredImportSelectorGrouping#getImports
  • AutoConfigurationImportSelector#getAutoConfigurationEntry
  • 实战自定义starter
  • 总结

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 {

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

}

可以看到@SpringBootApplication属于符合注解,由多个元注解组成,简单介绍一下几个较为复杂的元注解:

  • @SpringBootConfiguration
    @SpringBootConfiguration等价于@Configuration

  • @EnableAutoConfiguration
    复合注解,由@AutoConfigurationPackage和
    @Import组成,@AutoConfigurationPackage主要作用是解析出当前主类所在的包名,@Import则是导入AutoConfigurationImportSelector(实现DeferredImportSelector接口,DeferredImportSelector继承ImportSelector)

  • ComponentScan
    扫描包名、自定义排除Filter、包含Filter等;

2、DeferredImportSelectorHandler.handle

还记得上一篇文章中这张图?
在这里插入图片描述
由上面@SpringBootApplication注解可以看到,AutoConfigurationImportSelector(作为DeferredImportSelector的实现类)由@Import导入,因此在处理@Import注解时将会被特殊处理,执行DeferredImportSelectorHandler的hanle方法;
在这里插入图片描述

可以看到handle将AutoConfigurationImportSelector存入deferredImportSelectors中。

  • DeferredImportSelectorHandler.process
		public void process() {
			List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
			this.deferredImportSelectors = null;
			try {
				if (deferredImports != null) {
					DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
					//1、DeferredImportSelectorHolder排序
					deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
					//2、将DeferredImportSelectorHolder注册到DeferredImportSelectorGroupingHandler
					deferredImports.forEach(handler::register);
					//3、处理自动装配
					handler.processGroupImports();
				}
			}
			finally {
				this.deferredImportSelectors = new ArrayList<>();
			}
		}

接下来看一下register方法,主要将deferredImport按ImportGroup分类,然后再存入grouping中。

  • DeferredImportSelectorHandler.register
	public void register(DeferredImportSelectorHolder deferredImport) {
			//自动装配deferredImport.getImportSelector为AutoConfigurationImportSelector,getImportGroup为AutoConfigurationGroup
			Class<? extends Group> group = deferredImport.getImportSelector().getImportGroup();
			//computeIfAbsent不覆盖 返回覆盖后的
			DeferredImportSelectorGrouping grouping = this.groupings.computeIfAbsent(
					(group != null ? group : deferredImport),
					key -> new DeferredImportSelectorGrouping(createGroup(group)));
			//将deferredImport加到DeferredImportSelectorGrouping里面的deferredImports list里面
			grouping.add(deferredImport);
			this.configurationClasses.put(deferredImport.getConfigurationClass().getMetadata(),
					deferredImport.getConfigurationClass());
		}

然后接下来再看handler.processGroupImports()方法:

			public void processGroupImports() {
			for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
				//getImports得到需要导入的类
				grouping.getImports().forEach(entry -> {
					ConfigurationClass configurationClass = this.configurationClasses.get(entry.getMetadata());
					try {
					/**
						 * 将自动装配导入的类执行import处理逻辑,通过import处理逻辑标记当前自动装配类由configurationClass导入
						 * 便于后续ConfigurationClassBeanDefinitionReader.loadBeanDefinitions处理:如果当前ConfigurationClass是被import,
						 * 则通过registerBeanDefinitionForImportedConfigurationClass生成beanDefinition注册到beanDefinitionMap中
						 *  
						 *  则自动装配装配的是beanDefiniton,
						 */
						processImports(configurationClass, asSourceClass(configurationClass),
								asSourceClasses(entry.getImportClassName()), false);
					}
					catch (BeanDefinitionStoreException ex) {
						throw ex;
					}
					catch (Throwable ex) {
			         //省略非关键代码
					}
				});
			}
		}

- DeferredImportSelectorGrouping.getImports

public Iterable<Group.Entry> getImports() {
			for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
				        //执行process
	                    this.group.process(deferredImport.getConfigurationClass().getMetadata(),//	this.group.process
						deferredImport.getImportSelector());
			}
			return this.group.selectImports();
		}

接下来重点看AutoConfigurationGroup的process方法。

3、DeferredImportSelectorGrouping#getImports

		public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {

			AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
	          /**
			 * getAutoConfigurationMetadata:
			 * 加载META-INF/spring-autoconfigure-metadata.properties中的元素,找到自动装配类注入容器所需要依赖的类;*/					.getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata);
			this.autoConfigurationEntries.add(autoConfigurationEntry);
			//将自动装配类注入类以及引入的元注解存入map
			for (String importClassName : autoConfigurationEntry.getConfigurations()) {
				this.entries.putIfAbsent(importClassName, annotationMetadata);
			}
			}
		}

4、AutoConfigurationImportSelector#getAutoConfigurationEntry

在这里插入图片描述
getAutoConfigurationEntry方法可以分成6步:
A、获取@EnableAutoConfiguration注解属性;
B、从当前classPath下面获取所有META-INF下
spring.factories中EnableAutoConfiguration所对应的value作为自动装配的候选类;
C、利用hashSet对候选类进行去重;
D、对候选类排除@EnableAutoConfiguration注解中exclude和excludeName所对应的类;
E、利用filter方法对候选类进行过滤;
F、发布装配导入事件;

接下来重点看filter过滤方法:
在这里插入图片描述
总共有三种类型判断:OnClassCondition、OnBeanCondition、OnWebApplicationCondition,此处只以OnClassCondition为例,其他类似;
在这里插入图片描述
OnClassCondition:
在这里插入图片描述
先判断是否为多核,如果是多核则将候选类列表一分为二,启动新线程执行前半段,主线程解析后半段。解析完毕后则将解析结果复制到最终结果中,解析来重点关注如何解析:
在这里插入图片描述
候选类+ConditionalOnClass作为key从spring-autoconfigure-metada.properties文件中获取当前候选类加载到容器所需要的依赖类。
在这里插入图片描述
通过ClassNameFilter.MISSING.matches判断依赖类是否存在:
在这里插入图片描述
通过类加载器能否成功加载依赖类判断依赖类是否存在。如果成功加载,则说明依赖类存在,则候选类可以加载到容器。

以上的流程可以通过下图进行总结:
在这里插入图片描述

5、实战自定义starter

  • 自定义starter工程

在这里插入图片描述

  • 线程池参数
    在这里插入图片描述
  • 线程池Bean
    在这里插入图片描述
  • 装配bean加入spring.factories文件中
    在这里插入图片描述
    将以上生成如下jar包:在这里插入图片描述

接下来测试自动装配:

  • 测试demo

在这里插入图片描述

  • 配置线程池参数

thread.config.core-pool-size=8
thread.config.maximum-pool-size=64
thread.config.keep-alive-time=60

  • 注入线程池
    在这里插入图片描述
  • 启动

在这里插入图片描述

  • 结果

在这里插入图片描述

6、总结

在上一节分析@Import的基础上,本节对自动装配原理进行分析,其原理可以归纳为:

A、@EnableAutoConfiguration注解引入AutoConfigurationImportSelector类;

B、获取所有spring.factories文件中的org.springframework.boot.autoconfigure.EnableAutoConfiguration所对应的value作为自动装配类的候选列表;

C 、获取spring.factories中org.springframework.boot.autoconfigure.AutoConfigurationImportFilter中所对应的value作为过滤器Filter(OnBeanCondition、OnClassCondition、OnWebApplicationCondition);

D、加载spring-autoconfig-metadata.properties获得每个候选类所需要依赖类或者依赖bean;

E 、针对每个候选类,依次用每个过滤filter判断候选类是否匹配。如果所有Filter匹配,则作为可装配类返回;

F、遍历可装配类列表,执行processImports逻辑,标记当装配类是通过import导入。在ConfigurationClassBeanDefinitionReader.loadBeanDefinitions中,如果判断为通过import导入,则执行ConfigurationClassBeanDefinitionReader.registerBeanDefinitionForImportedConfigurationClass将自动装配类生成beanDefinition注册到beanDefinitionMap中;

通过以上梳理,则可以回答上一节的两个问题:
如何自动?
自动是通过@EnableAutoConfiguration注解实现;

装配什么?
装配的是beanDefiniton(将可装配类的beanDefinition注册到beanDefinitionMap中)。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值