2020-06-08

Version:1.0StartHTML:000000206EndHTML:000033034StartFragment:000011972EndFragment:000032956StartSelection:000011975EndSelection:000032952SourceURL:https://segmentfault.com/a/1190000022849494

自动配置(启动流程)

概念:能够在我们添加jar包依赖的时候,自动为我们配置一些组件的相关配置,我们无需配置或者只需要少量配置就能运行编写的项目

问题:Spring Boot到底是如何进行自动配置的,都把哪些组件进行了自动配置?

Spring Boot应用的启动入口是@SpringBootApplication注解标注类中的main()方法,        @SpringBootApplication能够扫描Spring组件并自动配置Spring Boot

下面,查看@SpringBootApplication内部源码进行分析 ,核心代码具体如下

@SpringBootApplication

public class SpringbootDemoApplication {

public static void main(String[] args) {

SpringApplication.run(SpringbootDemoApplication.class, args);

}

}

12345678910111213141516

@Target({ElementType.TYPE}) //注解的适用范围,Type表示注解可以描述在类、接口、注解或枚举中

@Retention(RetentionPolicy.RUNTIME) //表示注解的生命周期,Runtime运行时

@Documented //表示注解可以记录在javadoc中

@Inherited

//表示可以被子类继承该注解

@SpringBootConfiguration    // 标明该类为配置类

@EnableAutoConfiguration    // 启动自动配置功能

@ComponentScan(                // 包扫描器

excludeFilters = {@Filter(

type = FilterType.CUSTOM,

classes = {TypeExcludeFilter.class}

), @Filter(

type = FilterType.CUSTOM,

classes = {AutoConfigurationExcludeFilter.class}

)}

)

public @interface SpringBootApplication {

...

}

1234567891011121314151617181920212223242526272829303132333435363738394041424344

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

1.@SpringBootConfiguration注解

@SpringBootConfiguration注解表示Spring Boot配置类。查看@SpringBootConfiguration注解源码,核心代码具体如下。

@Target({ElementType.TYPE})

@Retention(RetentionPolicy.RUNTIME)

@Documented

@Configuration //配置IOC容器

public @interface SpringBootConfiguration {

}

123456789101112

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

2.@EnableAutoConfiguration注解

@EnableAutoConfiguration注解表示开启自动配置功能,该注解是Spring

Boot框架最重要的注解,也是实现自动化配置的注解。同样,查看该注解内部查看源码信息,核心代码具体如下

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VNiEwu0S-1591339714519)(./images/image-20191226121755878.png)]

可以发现它是一个组合注解,Spring 中有很多以Enable开头的注解,其作用就是借助@Import来收集并注册特定场景相关的bean,并加载到IoC容器。@EnableAutoConfiguration就是借助@Import来收集所有符合自动配置条件的bean定义,并加载到IoC容器。

下面,对这两个核心注解分别讲解 :

(1)@AutoConfigurationPackage注解

查看@AutoConfigurationPackage注解内部源码信息,核心代码具体如下:

@Target({ElementType.TYPE})

@Retention(RetentionPolicy.RUNTIME)

@Documented

@Inherited

@Import({Registrar.class})      // 导入Registrar中注册的组件

public @interface AutoConfigurationPackage

{

}

123456789101112131415

从上述源码可以看出,@AutoConfigurationPackage注解的功能是由@Import注解实现的,它是spring框架的底层注解,它的作用就是给容器中导入某个组件类,例如@Import(AutoConfigurationPackages.Registrar.class),它就是将Registrar这个组件类导入到容器中,可查看Registrar类中registerBeanDefinitions方法,这个方法就是导入组件类的具体实现 :

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lTVprz3I-1591339714522)(./images/image-20191226122946037.png)]

从上述源码可以看出,在Registrar类中有一个registerBeanDefinitions()方法,使用Debug模式启动项目,      可以看到选中的部分就是com.lagou。也就是说,@AutoConfigurationPackage注解的主要作用就是将主程序类所在包及所有子包下的组件到扫描到spring容器中。

因此 在定义项目包结构时,要求定义的包结构非常规范,项目主程序启动类要定义在最外层的根目录位置,然后在根目录位置内部建立子包和类进行业务开发,这样才能够保证定义的类能够被组件扫描器扫描

(2)@Import({AutoConfigurationImportSelector.class}):将AutoConfigurationImportSelector这个类导入到spring容器中,AutoConfigurationImportSelector可以帮助springboot应用将所有符合条件的@Configuration配置都加载到当前SpringBoot创建并使用的IoC容器(ApplicationContext)中

继续研究AutoConfigurationImportSelector这个类,通过源码分析这个类中是通过selectImports这个方法告诉springboot都需要导入那些组件:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-45peKDLA-1591339714523)(./images/image-20200119172227144.png)]

深入研究loadMetadata方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pezQSIoU-1591339714525)(./images/image-20200119172325325.png)]

深入getCandidateConfigurations方法

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CqEWt2LP-1591339714526)(./images/image-20200119172418984.png)]

继续点开loadFactory方法

public static List

loadFactoryNames(Class factoryClass, @Nullable ClassLoader

classLoader) {

//获取出入的键

String factoryClassName = factoryClass.getName();

return

(List)loadSpringFactories(classLoader).getOrDefault(factoryClassName,

Collections.emptyList());

}

private static Map>

loadSpringFactories(@Nullable ClassLoader classLoader) {

MultiValueMap result =

(MultiValueMap)cache.get(classLoader);

if (result != null) {

return result;

} else {

try {

//如果类加载器不为null,则加载类路径下spring.factories文件,将其中设置的配置类的全路径信息封装

为Enumeration类对象

Enumerationurls =

classLoader != null ?

classLoader.getResources("META-INF/spring.factories") :

ClassLoader.getSystemResources("META-INF/spring.factories");

LinkedMultiValueMapresult =

new LinkedMultiValueMap();

//循环Enumeration类对象,根据相应的节点信息生成Properties对象,通过传入的键获取值,在将值切割为一个个小的字符串转化为Array,方法result集合中while(urls.hasMoreElements()) {URLurl =

(URL)urls.nextElement();

UrlResource resource =new

UrlResource(url);

Propertiesproperties=

PropertiesLoaderUtils.loadProperties(resource);

Iteratorvar6 =

properties.entrySet().iterator();

while(var6.hasNext()) {                    Entry<?,?>entry

= (Entry)var6.next();

StringfactoryClassName

= ((String)entry.getKey()).trim();

String[] var9 =

StringUtils.commaDelimitedListToStringArray((String)entry.getValue());

intvar10=

var9.length;

for(intvar11=0;

var11 < var10; ++var11) {

StringfactoryName

= var9[var11];

result.add(factoryClassName, factoryName.trim());

}                }            }            cache.put(classLoader, result);returnresult;

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112

会去读取一个 sprin  g.factories 的文件,读取不到会表这个错误,我们继续根据会看到,最终路径的长这样,而这个是spring提供的一个工具类

public final class SpringFactoriesLoader {

public static final String FACTORIES_RESOURCE_LOCATION =

"META-INF/spring.factories";

}

12345678

它其实是去加载一个外部的文件,而这文件是在

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8dBXcvgJ-1591339714528)(./images/image-20191226162644636.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-s8ckWPkq-1591339714530)(./images/image-20191226162832498.png)]

@EnableAutoConfiguration就是从classpath中搜寻META-INF/spring.factories配置文件,并将其中org.springframework.boot.autoconfigure.EnableutoConfiguration对应的配置项通过反射(Java Refletion)实例化为对应的标注了@Configuration的JavaConfig形式的配置类,并加载到IOC容器中

以刚刚的项目为例,在项目中加入了Web环境依赖启动器,对应的WebMvcAutoConfiguration自动配置类就会生效,打开该自动配置类会发现,在该配置类中通过全注解配置类的方式对Spring MVC运行所需环境进行了默认配置,包括默认前缀、默认后缀、视图解析器、MVC校验器等。而这些自动配置类的本质是传统Spring MVC框架中对应的XML配置文件,只不过在Spring Boot中以自动配置类的形式进行了预先配置。因此,在Spring Boot项目中加入相关依赖启动器后,基本上不需要任何配置就可以运行程序,当然,我们也可以对这些自动配置类中默认的配置进行更改

总结

因此springboot底层实现自动配置的步骤是:

springboot应用启动;

@SpringBootApplication起作用;

@EnableAutoConfiguration;

@AutoConfigurationPackage:这个组合注解主要是@Import(AutoConfigurationPackages.Registrar.class),它通过将Registrar类导入到容器中,而Registrar类作用是扫描主配置类同级目录以及子包,并将相应的组件导入到springboot创建管理的容器中;

@Import(AutoConfigurationImportSelector.class):它通过将AutoConfigurationImportSelector类导入到容器中,AutoConfigurationImportSelector类作用是通过selectImports方法执行的过程中,会使用内部工具类SpringFactoriesLoader,查找classpath上所有jar包中的META-INF/spring.factories进行加载,实现将配置类信息交给SpringFactory加载器进行一系列的容器创建过程

@ComponentScan注解

@ComponentScan注解具体扫描的包的根路径由Spring Boot项目主程序启动类所在包位置决定,在扫描过程中由前面介绍的@AutoConfigurationPackage注解进行解析,从而得到Spring

Boot项目主程序启动类所在包的具体位置

总结:

@SpringBootApplication 的注解的功能就分析差不多了, 简单来说就是 3 个注解的组合注解:学习让人快乐,学习更让人觉得无知!

学了1个多月的《Java工程师高薪训练营》,才发现自己对每个技术点的认知都很肤浅,根本深不下去,立个Flag:每天坚持学习一小时,一周回答网上3个技术问题,把自己知道都分享出来。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,802评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,109评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,683评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,458评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,452评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,505评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,901评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,550评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,763评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,556评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,629评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,330评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,898评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,897评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,140评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,807评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,339评论 2 342