SpringBoot 源码解析 (五)----- Spring Boot的核心能力 - 自动配置源码解析
在上一篇博客中分析了springboot
启动流程,大体的轮廓只是冰山一角。今天就来看一下springboot
的亮点功能:自动化装配功能。
先从@springbootapplication
开始。在启动流程章节中,我们讲述了springboot2大致的启动步骤,并进行了源码详解。但是在刷新容器这块并未展开,refreshcontext(context);简单的一行代码,背后却做了太多事情。所以为了不喧宾夺主,本篇也尽量选取和注解@springbootapplication有关的方法讲解。
springboot启动类加载
首先加载springboot启动类注入到spring容器中beandefinitionmap中,看下preparecontext方法中的load方法:load(context, sources.toarray(new object[0]));
跟进该方法最终会执行beandefinitionloader的load方法:
private int load(object source) { assert.notnull(source, "source must not be null"); //如果是class类型,启用注解类型 if (source instanceof class<?>) { return load((class<?>) source); } //如果是resource类型,启用xml解析 if (source instanceof resource) { return load((resource) source); } //如果是package类型,启用扫描包,例如:@componentscan if (source instanceof package) { return load((package) source); } //如果是字符串类型,直接加载 if (source instanceof charsequence) { return load((charsequence) source); } throw new illegalargumentexception("invalid source type " + source.getclass()); }
继续跟进load(class<?> source)
方法:
上述方法判断启动类中是否包含@component注解,可我们的启动类并没有该注解。继续跟进会发现,annotationutils判断是否包含该注解是通过递归实现,注解上的注解若包含指定类型也是可以的。
启动类中包含@springbootapplication注解,进一步查找到@springbootconfiguration注解,然后查找到@component注解,最后会查找到@component注解:
@target(elementtype.type) @retention(retentionpolicy.runtime) @documented @component public @interface configuration { }
在查找到@component
注解后,表面该对象为spring bean,然后会将其信息包装成 beandefinitaion ,添加到容器的 beandefinitionmap中。如下:
如此一来,我们的启动类就被包装成annotatedgenericbeandefinition
了,后续启动类的处理都基于该对象了。
@enableautoconfiguration
@springbootapplication注解中包含了自动配置的入口注解:
@springbootconfiguration @enableautoconfiguration @componentscan(excludefilters = { @filter(type = filtertype.custom, classes = typeexcludefilter.class), @filter(type = filtertype.custom, classes = autoconfigurationexcludefilter.class) }) public @interface springbootapplication {}
我们跟进去看看@enableautoconfiguration
@autoconfigurationpackage @import(enableautoconfigurationimportselector.class) public @interface enableautoconfiguration {}
@autoconfigurationpackage
- 自动配置包注解
@import(autoconfigurationpackages.registrar.class) public @interface autoconfigurationpackage {}
@import(autoconfigurationpackages.registrar.class):默认将主配置类(@springbootapplication)所在的包及其子包里面的所有组件扫描到spring容器中。如下
@order(ordered.highest_precedence) static class registrar implements importbeandefinitionregistrar, determinableimports { @override public void registerbeandefinitions(annotationmetadata metadata, beandefinitionregistry registry) { //默认将会扫描@springbootapplication标注的主配置类所在的包及其子包下所有组件 register(registry, new packageimport(metadata).getpackagename()); } @override public set<object> determineimports(annotationmetadata metadata) { return collections.<object>singleton(new packageimport(metadata)); } }
@import(enableautoconfigurationimportselector.class)
enableautoconfigurationimportselector: 导入哪些组件的选择器,将所有需要导入的组件以全类名的方式返回,这些组件就会被添加到容器中。
1 //enableautoconfigurationimportselector的父类:autoconfigurationimportselector 2 @override 3 public string[] selectimports(annotationmetadata annotationmetadata) { 4 if (!isenabled(annotationmetadata)) { 5 return no_imports; 6 } 7 try { 8 autoconfigurationmetadata autoconfigurationmetadata = autoconfigurationmetadataloader 9 .loadmetadata(this.beanclassloader); 10 annotationattributes attributes = getattributes(annotationmetadata); 11 list<string> configurations = getcandidateconfigurations(annotationmetadata, attributes); 12 configurations = removeduplicates(configurations); 13 configurations = sort(configurations, autoconfigurationmetadata); 14 set<string> exclusions = getexclusions(annotationmetadata, attributes); 15 checkexcludedclasses(configurations, exclusions); 16 configurations.removeall(exclusions); 17 configurations = filter(configurations, autoconfigurationmetadata); 18 fireautoconfigurationimportevents(configurations, exclusions); 19 return configurations.toarray(new string[configurations.size()]); 20 } 21 catch (ioexception ex) { 22 throw new illegalstateexception(ex); 23 } 24 }
我们主要看第11行list<string> configurations = getcandidateconfigurations(annotationmetadata, attributes);
会给容器中注入众多的自动配置类(xxxautoconfiguration),就是给容器中导入这个场景需要的所有组件,并配置好这些组件。获取这些组件后,还要过滤一下这些组件,我们跟进去看看
protected list<string> getcandidateconfigurations(annotationmetadata metadata, annotationattributes attributes) { list<string> configurations = springfactoriesloader.loadfactorynames(getspringfactoriesloaderfactoryclass(), getbeanclassloader()); //... return configurations; } protected class<?> getspringfactoriesloaderfactoryclass() { return enableautoconfiguration.class; } public static final string factories_resource_location = "meta-inf/spring.factories"; public static list<string> loadfactorynames(class<?> factoryclass, classloader classloader) { string factoryclassname = factoryclass.getname(); try { //从类路径的meta-inf/spring.factories中加载所有默认的自动配置类 enumeration<url> urls = (classloader != null ? classloader.getresources(factories_resource_location) : classloader.getsystemresources(factories_resource_location)); list<string> result = new arraylist<string>(); while (urls.hasmoreelements()) { url url = urls.nextelement(); properties properties = propertiesloaderutils.loadproperties(new urlresource(url)); //获取enableautoconfiguration指定的所有值,也就是enableautoconfiguration.class的值 string factoryclassnames = properties.getproperty(factoryclassname); result.addall(arrays.aslist(stringutils.commadelimitedlisttostringarray(factoryclassnames))); } return result; } catch (ioexception ex) { throw new illegalargumentexception("unable to load [" + factoryclass.getname() + "] factories from location [" + factories_resource_location + "]", ex); } }
springboot启动的时候从类路径下的 meta-inf/spring.factories中获取enableautoconfiguration指定的值,并将这些值作为自动配置类导入到容器中,自动配置类就会生效,最后完成自动配置工作。enableautoconfiguration默认在spring-boot-autoconfigure这个包中,如下图
最终有96个自动配置类被加载并注册进spring容器中
我们也可以将需要自动配置的bean写入这个文件
自定义starter
首先定义一个配置类模块:
@configuration @conditionalonproperty(name = "enabled.autoconfituration", matchifmissing = true) public class myautoconfiguration { static { system.out.println("myautoconfiguration init..."); } @bean public simplebean simplebean(){ return new simplebean(); } }
然后定义一个starter模块,里面无需任何代码,pom也无需任何依赖,只需在meta-inf下面建一个 spring.factories
文件,添加如下配置:
org.springframework.boot.autoconfigure.enableautoconfiguration=\ spring.study.startup.bean.myautoconfiguration
如图所示:
最后只需在启动类项目的pom中引入我们的 starter 模块即可。
原理
最终在autoconfigurationimportselector
解析spring.factories
文件:
springboot为我们提供的配置类有180多个,但是我们不可能会全部引入。按条件注解 @conditional或者@conditionalonproperty等相关注解进行判断,决定是否需要装配。
我们自定义的配置类也是以相同的逻辑进行装配,我们指定了以下注解:
@conditionalonproperty(name = "enabled.autoconfituration", matchifmissing = true)
默认为 true,所以自定义的starter成功执行。
上一篇: spring boot1.1 idea + springboot + mybatis(mybatis-generator) +mysql +html实现简单的登录注册
下一篇: 2019年腾讯最新Java工程师面试题
推荐阅读
-
SpringBoot 源码解析 (七)----- Spring Boot的核心能力 - SpringBoot如何实现SpringMvc的?
-
SpringBoot 源码解析 (六)----- Spring Boot的核心能力 - 内置Servlet容器源码分析(Tomcat)
-
SpringBoot 源码解析 (九)----- Spring Boot的核心能力 - 整合Mybatis
-
SpringBoot 源码解析 (五)----- Spring Boot的核心能力 - 自动配置源码解析
-
SpringBoot 源码解析 (六)----- Spring Boot的核心能力 - 内置Servlet容器源码分析(Tomcat)
-
SpringBoot 源码解析 (七)----- Spring Boot的核心能力 - SpringBoot如何实现SpringMvc的?
-
SpringBoot 源码解析 (五)----- Spring Boot的核心能力 - 自动配置源码解析
-
SpringBoot 源码解析 (九)----- Spring Boot的核心能力 - 整合Mybatis