Spring Boot自动装配原理源码分析
Spring Boot自动装配原理源码分析
1.环境准备
使用IDEA Spring Initializr快速创建一个Spring Boot项目


添加一个Controller类
@RestController
public class HelloController {
    @RequestMapping("hello")
    public String hello() {
        return "hello";
    }
}
主配置类如下
@SpringBootApplication
public class SpringbootQuickstartApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringbootQuickstartApplication.class, args);
    }
}
2.注解分析
Spring Boot规定,项目的主配置类必须放在最外层包,也就是说,所有的类都必须放在主配置类的同级包或者子包里,这么做的用意是什么?我们点开@SpringBootApplication注解慢慢分析(下面代码中省略元注解)...
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
	@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
	@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class)
})
public @interface SpringBootApplication {
@SpringBootApplication内部标注了三个注解:
@SpringBootConfiguration
进入源码中可以看见,
@SpringBootConfiguration其实就是Spring中的@Configuration,用于标注配置类@Configuration
public @interface SpringBootConfiguration {
@ComponentScan
这个注解也是Spring中的,它用来将指定包下需要装配的组件注册到容器中
@EnableAutoConfiguration
接下来才是今天的重头戏,Spring Boot自动配置的主角!
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
3.自动装配的主角
	进入@EnableAutoConfiguration源码你会发现这个注解中标注了两个注解@AutoConfigurationPackage和@Import
(1)、@AutoConfigurationPackage
点进该注解
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
}
在点进Register,这是一个静态内部类
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));
  }
}
我们在第一个方法处打一个断点debug一下,发现new PackageImport(metadata).getPackageName()的结果其实就是一个包名,这时我们很容易的可以想到,这个包就是Spring Boot主配置类所在的包

再看一眼metadata,果然,就是主配置类

因此,这个注解的作用就是将主配置类所在的包作为自动配置包进行管理
(2)、@Import(AutoConfigurationImportSelector.class)
@Import的作用就是导入一个类到IOC容器,我们先来看一下导入的这个类:自动配置导入选择器
源码里有一个方法selectImports,选择导入
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
   if (!isEnabled(annotationMetadata)) {
      return NO_IMPORTS;
   }
   AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
         .loadMetadata(this.beanClassLoader);
   AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
         annotationMetadata);
   return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
在点进getAutoConfigurationEntry,获取自动配置类
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
      AnnotationMetadata annotationMetadata) {
   if (!isEnabled(annotationMetadata)) {
      return EMPTY_ENTRY;
   }
   AnnotationAttributes attributes = getAttributes(annotationMetadata);
   List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
   configurations = removeDuplicates(configurations);
   Set<String> exclusions = getExclusions(annotationMetadata, attributes);
   checkExcludedClasses(configurations, exclusions);
   configurations.removeAll(exclusions);
   configurations = filter(configurations, autoConfigurationMetadata);
   fireAutoConfigurationImportEvents(configurations, exclusions);
   return new AutoConfigurationEntry(configurations, exclusions);
}
在getCandidateConfigurations方法处打一个断点,发现configurations的结果是所有的xxxAtuoConfiguration类,一共124个,请记住这个数字。

那么这些自动配置类是如何获取的呢,从哪里获取的呢?
我们继续点进getCandidateConfigurations,获取候选的配置
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
	List<String> configurations =
        SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
         getBeanClassLoader());
   Assert.notEmpty(configurations, "... ...");
   return configurations;
}
继续点,loadFactoryNames,加载工厂名,方法所在类中有一个常量FACTORIES_RESOURCE_LOCATION,看代码可以清晰的看到,这方法加载classpath下的所有jar包的META-INF/spring.factories文件,结果用一个HashMap存储
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();
		return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
	}
	private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
		MultiValueMap<String, String> result = cache.get(classLoader);
		if (result != null) {
			return result;
		}
		try {
			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());
					}
				}
			}
			cache.put(classLoader, result);
			return result;
		}
		catch (IOException ex) {
			throw new IllegalArgumentException("Unable to load factories from location [" +
					FACTORIES_RESOURCE_LOCATION + "]", ex);
		}
	}
打开spring-boot-autoconfigure-2.2.4.RELEASE.jar/META-INF/spring.factories,文件部分类容如下,你可以点进去看看第22~145行,确实是124个全类名
# 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.CloudServiceConnectorsAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\
org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\
org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveRepositoriesAutoConfiguration
... ...
4.类的加载时机
加载了这么多类我们又不一定全都用得到,设计师肯定会想办法让类在我们需要的时候才生效,我们随便点进一个类,可以看到一片飘红,因为我们并没有引入RabbitMQ相关依赖,再看一个注解ConditionalOnClass,意思就是存在某个指定的类才生效,类似的注解还有很多,都是@ConditionaOn xxx,在一定条件下类才会生效。

由于引入了web模块,WebMvcAutoConfiguration正常显示

Spring Boot自动装配原理源码分析的更多相关文章
- Spring Boot 揭秘与实战 源码分析 - 工作原理剖析
		
文章目录 1. EnableAutoConfiguration 帮助我们做了什么 2. 配置参数类 – FreeMarkerProperties 3. 自动配置类 – FreeMarkerAutoCo ...
 - Spring Boot 揭秘与实战 源码分析 - 开箱即用,内藏玄机
		
文章目录 1. 开箱即用,内藏玄机 2. 总结 3. 源代码 Spring Boot提供了很多”开箱即用“的依赖模块,那么,Spring Boot 如何巧妙的做到开箱即用,自动配置的呢? 开箱即用,内 ...
 - Spring Boot自动装配
		
前言 一些朋友问我怎么读源码,这篇文章结合我看源码时候一些思路给大家聊聊,我主要从这三个方向出发: 确定目标,这个目标要是一个具体,不要一上来我要看懂Spring,这是不可能的,目标要这么来定,比如看 ...
 - Spring Boot 自动装配流程
		
Spring Boot 自动装配流程 本文以 mybatis-spring-boot-starter 为例简单分析 Spring Boot 的自动装配流程. Spring Boot 发现自动配置类 这 ...
 - Spring Boot系列(二):Spring Boot自动装配原理解析
		
一.Spring Boot整合第三方组件(Redis为例) 1.加依赖 <!--redis--> <dependency> <groupId>org.springf ...
 - Spring Boot 自动装配(二)
		
目录 目录 前言 1.起源 2.Spring Boot 自动装配实现 2.1.@EnableAutoConfiguration 实现 2.1.1. 获取默认包扫描路径 2.1.2.获取自动装配的组件 ...
 - Spring Boot 自动装配原理
		
Spring Boot 自动装配原理 Spring Boot 在启动之前还有一系列的准备工作,比如:推断 web 应用类型,设置初始化器,设置监听器,启动各种监听器,准备环境,创建 applicati ...
 - SpringBoot自动配置原理源码级别分析
		
SpringBoot自动配置原理 前言 后面还会讲到SpringBoot自动配置原理,会主要讲解@EnableAutoConfiguratuon注解帮助我们做了什么事情,是如何自动把自动配置类扫描到容 ...
 - 从源码中理解Spring Boot自动装配原理
		
个人博客:槿苏的知识铺 一.什么是自动装配 SpringBoot 定义了一套接口规范,这套规范规定:SpringBoot在启动时会扫描外部引用jar包中的META-INF/spring.factori ...
 
随机推荐
- ScheduledThreadPoolExecutor中定时周期任务的实现源码分析
			
ScheduledThreadPoolExecutor是一个定时任务线程池,相比于ThreadPoolExecutor最大的不同在于其阻塞队列的实现 首先看一下其构造方法: public Schedu ...
 - java中高级并发SPI机制
			
Java SPI 实际上是“基于接口的编程+策略模式+配置文件”组合实现的动态加载机制. 适用于:调用者根据实际使用需要,启用.扩展.或者替换框架的实现策略. 要使用Java SPI,需要遵循如下约定 ...
 - python常用英语单词(初学,英语不好的适用)
			
对于刚才是学习python这些也足够了,一天学个六七个单词记一下在配合自己寻找的视频.书籍等等方法去学习是有一定帮助的. 这里还是要说一句,仅供兴趣爱好学习使用,个人开发者(非考虑未来靠此为生的人士) ...
 - 原生javascript实现二级延时菜单
			
一.实现原理: 使用定时器和排他思想完成 二.代码: <!DOCTYPE html> <html> <head> <title></title&g ...
 - Spring中常见的设计模式——适配器模式
			
一.适配器模式的应用场景 适配器模式(Adapter Pattern)是指将一个类的接口转换成用户期待的另一个接口,使原本接口不兼容的类可以一起工作,属于构造设计模式. 适配器适用于以下几种业务场景: ...
 - YOLO V3训练自己的数据集
			
数据的输入几乎和Faster rcnn一样,标签格式xml是一样的. 相比Faster rcnn,数据多了一步处理,通过voc_annotation.py将图片路径和bbox+class存储在txt下 ...
 - python利用sift和surf进行图像配准
			
1.SIFT特征点和特征描述提取(注意opencv版本) 高斯金字塔:O组L层不同尺度的图像(每一组中各层尺寸相同,高斯函数的参数不同,不同组尺寸递减2倍) 特征点定位:极值点 特征点描述:根据不同b ...
 - springcloud复习1
			
1.SpringCloud是什么?SpringCloud=分布式微服务架构下的一站式解决方案,是各个微服务架构落地技术的集合体,俗称微服务全家桶. 2.SpringCloud和SpringBoot是什 ...
 - jqGrid以setGridParam方式postData,包含历史数据的问题
			
系统在使用jqGrid时,如果某些页面的查询项是复选框,后台是数组接收的,就会出现传值不正确问题. 1.项目中某查询页面存在的复选框:待处理S1,正在处理S2,已处理S3: 使用jqGrid提交查询数 ...
 - C# 调用word进程操作文档关闭进程
			
C# 调用word进程操作文档关闭进程 作者:Jesai 时间:2018-02-12 20:36:23 前言: office办公软件作为现在主流的一款办公软件,在我们的日常生活和日常工作里面几乎每天都 ...