SpringBoot学习(9)(springboot自动配置原理)(源码分析、面试题)

目录

一、引言

二、为啥学习自动配置原理?

三、自动配置

(1)基本概述

(2)学习回顾

四、自动配置——源码分析

(1)回顾学习

(2)回到源码学习

(1)注解@ComponentScan

(2)注解@SpringBootConfiguration

(3)核心注解@EnableAutoConfiguration

(4)对刚刚源码的一个大致总结(梳理)

五、解决问题

六、分析总结

(1)小总结

(2)面试(总结)

一、引言

  • 之前学习了Bean的扫描、Bean的注册、以及Bean的注册条件。
  • 现在在这篇博客学习springboot的自动配置原理

二、为啥学习自动配置原理?

  • 在实际的开发中,经常会定义一些公共的组件,供大家一起使用。为了使用更加的方便,经常会将这些公共的组件自定义成starter。如果想定义starter,就必须先了解自动配置原理才可以。
  • 应对面试。springboot作为当下市面上最流行的Java技术之一。面试题:请说一下springboot自动配置的原理?

三、自动配置

(1)基本概述
  • 所谓的自动配置。就是遵循约定大约配置的原则,在boot程序启动后,起步依赖中的一些bean对象会自动注入到ioc容器中。
(2)学习回顾
  • 之前学习bean扫描、bean注册、bean注册条件的时候——>其中学习的案例中通过导入一个自定义的jar包,jar包中有两个实体类(Country、Province),然后在自己的boot工程引入了这个jar包。想把Country和Province注入到Ioc容器当中——>自己提供写了一个配置类(CommonConfig),这里面提供了两个方法(country()、province())这两个方法分别用来注入Country对象、Province对象——>最后在启动类中使用注解@Import把这个配置类给导入进来——>这样就注入到Ioc容器当中了——>可是真的是自动配置了吗??

  • 之前学习springboot整合mybatis的时候,只用引入mybatis的起步依赖就行了,不用去手动的注入对应的bean对象(自动注入到Ioc容器当中),并没有写任何的配置

四、自动配置——源码分析

(1)回顾学习
  • 当程序引入 "spring-boot-starter-web" 起步依赖,启动后。会自动往Ioc容器中注入DispatcherServlet类
  • 验证是不是如上所说?
  • 首先pom文件中并没有引入web依赖(只引入了一个springboot的核心起步依赖)


  • 回到启动类。将程序启动后,将返回的Ioc容器接收一下。使用ApplicationContext context = SpringApplication.run(SpringbootAutoConfigApplication.class,args);然后用这个context对象调用方法getBean(String name),里面的name参数写上面要测试的类"dispatcherServlet"。然后把他打印输出到控制台。

  • 因为没有引入web的起步依赖,所以Ioc容器中并没有该指定类的对象。报错

  • 重新导入完web的起步依赖后(别忘了刷新一下Maven)再重新运行。发现获取成功!这就验证了只要引入web的起步依赖,它就会自动的往Ioc容器里面注入一个叫"dispatcherServlet"springboot是如何做到的??


(2)回到源码学习
  • 从启动类的@SpringBootApplication开始(这里之前学习Bean扫描时看到过)(鼠标停留在那,然后按ctrl进入到源码界面
  • 进去之后可以看到,注解@SpringBootApplication是一个组合注解,它组合了三个注解,如下:

(1)注解@ComponentScan

其中第三个注解@ComponentScan之前学习过,就是Bean扫描。(springboot默认只能扫描启动类所在的包及其子包。其它地方扫描不到。若想要扫描其它的包,可以手动的去添加注解@ComponentScan)


(2)注解@SpringBootConfiguration

(按住ctrl点进去)说明我们的启动类也是一个配置类??

(3)核心注解@EnableAutoConfiguration
  • 当看到这个@EnableXXX的时候,回想起之前学习注解@Import的时候,用更优雅的方式写进代码,使用组合注解——>@Enable+配置类名

(按住ctrl点进去)组合了两个注解


  • 重点关注@Import(导入注解)那行的注解。发现很熟悉"ImportSelector"。因为注解@Import经常导入两种类——>配置类、ImportSelector接口实现类


  • 现在再按住CTRL点击进来,再看到它实现"DeferredImportSelector"这个接口

  • 现在再按住CTRL点击进来,看这个接口它继承了"ImportSelector"这个接口。也就是说我们当前这个类"AutoConfigurationImportSelector"它是实现"ImportSelector"这个接口,那么必然会重写方法"selectImports()"

  • 例如之前案例学习的下面这种写法

  • 之前说过selectImports()方法,会被我们的springboot自动调用,从而得到它返回的全类名字符串数组,把对应的类的Bean对象注入到Ioc容器里。

  • 然后之前在selectImports()方法中,返回的全类名,我们并没有"写死",而是从配置文件中读取。在return返回回去。现在继续向下看,首先它return的是一个对象调用返回值,然后将其转换为字符串数组。现在核心就在"autoConfigurationEntry"这个对象上。

  • 因此需要猜想,这个方法getAutoConfigurationEntry()里面肯定要知道配置文件在哪里。然后现在跟踪这个方法——>返回的是一个新(new)的对象——>其中两个参数:
    "configurations"跟我们的配置相关

  • 然后又跟getCandidateConfigurations()方法有关,再继续跟进——>它调用了一个.load()方法,这是不是加载的意思。而且它后面的参数是一个类名"AutoConfiguration",后面会继续看到。往后再跟进就很复杂,先到这里继续看。
  • 其次还能往下看到底下有一段断言——>它是说:"这个configurations不能为空",如果为空了就出现下面的提示!!——>重点看下面的提示:"在这个METC-INF目录下的spring文件中有一串很长的配置文件.imports",以后将这个配置文件称为"dear imports配置文件"

  • 接着我们去寻找上面所说的配置文件。回到pom文件,当时引入了一个springboot的核心起步依赖。按住ctrl进去——>

  • 进入之后可以看到autoconfigure,顾名思义就是自动配置的意思

  • 接着来到左边查看第三方库,找到上面的autoconfigure,然后看到上面的目录METC-INF——>spring目录——>对应的"dear imports"配置文件——>打开看到之后全是一些全类名,程序会从里面读取,然后使用——>然后在配置文件中ctrl+f(搜索其中一个类名"DispatcherServletAutoConfiguration")这个类是不是很熟悉,之前引入web起步依赖,它会自动注入类"DispatcherServlet"的Bean对象进入Ioc容器——>接着我们点击进去看看——>这个类添加一个注解@AutoConfiguration,顾名思义就是自动配置的意思——>再点进去就看到它也是一个组合注解!——>其中有一个注解@Configuration,说的明白一点就是说明前面"DispatcherServletAutoConfiguration"这个类就是一个配置类,然后用上注解@AutoConfiguration就是为了完成自动配置的——>然后下面还有一个注解@ConditionalOnClass,这个是不是很熟悉(在bean注册条件里的),它在这的意思就是如果环境里面有"DispatcherServlet"这个类,这个时候这个自动配置类"DispatcherServletAutoConfiguration"起效果,将来就会自动的注入一个"DispatcherServlet"对象,如果环境里面没有,自动配置类就不生效,不注入了——>接着它里面还提供了一个内部的配置类。这个类里面提供了一个重要的方法——>方法的返回值类型是"DispatcherServlet"。方法的内部new了一个"DispatcherServlet"对象,而且这个方法添加了一个@Bean注解,而我们现在发现这个方法和之前自己的操作没什么区别!!——>就是都是声明一个方法,然后再声明一个注解@Bean——>而这个地方最核心的地方就是:它让这个类"DispatcherServletAutoConfiguration"放在指定的配置文件中,然后springboot就能够自动的读取到这个全类名,然后把这个配置类的对象注入到Ioc容器当中,而这个配置类当中还有一个配置类,而且内部的配置类中还有一些方法并添加注解@Bean,所以springboot它会自动的继续解析,直到把这些@Bean所注解的方法全部解析到,执行这些方法,把返回值注入到Ioc容器当中——>所以我们自动配置的核心就再这个配置文件当中!!!——>源码分析查看到这里吧。







(4)对刚刚源码的一个大致总结(梳理)
  • 从刚刚开始——>我们是从注解@SpringBootApplication开始的,它是一个组合注解,它组合了一个非常重要的注解@EnableAutoConfiguration,开启自动配置——>而这个@EnableAutoConfiguration也是一个组合注解,它组合了@Import注解,导入了一个"AutoConfigurationImportselector"这个类,这个类是"Importselector"的实现类,所以它里面重写了一个selectImports()方法,这个方法内部通过层层调用,会读取一个配置文件——>是一个xxx.spring.imports配置文件,在这个配置文件中,写了很多的全类名——>这些类都是自动配置类,其中有一个类"DispatcherServletAutoConfiguration",它是完成"DispatcherServlet"类Bean对象的自动注入的——>这个类"DispatcherServletAutoConfiguration"添加了两个注解,第一个是注解@AutoConfiguration,标识当前这个类是一个自动配置类。第二个是注解@Conditional0nClass,这个是标识用来设置Bean的注册条件,如果环境中有类"DispatcherServlet",那么这个自动配置类就自动生效,否则不生效——>也就是当引入了web起步依赖之后,我们的springboot就会帮忙自动的将一个类"DispatcherServlet"的bean对象注入到Ioc容器中——>而自动注入的代码核心就在底下,写了一个方法,方法的返回值类型就是类"DispatcherServlet",这个方法的返回值声明了一个注解@Bean

  • 说了这么多,自动配置的核心就是在这个".imports"配置文件当中。也就是在面试当中提到这个文件,就答案已经正确了一半了。但是在网络很多文章当中,把这个文件叫做"spring deer factories",因为在以前springboot的版本用的是其它的配置文件名。

五、解决问题

(1)前面提到的案例中是否自动配置问题??之前写的代码并没有达到自动配置的效果,因为需要手动的提供一个配置类,然后写注解@Import,这样很麻烦。如果想要自动达到配置的效果,那么这个配置类就不应该自己去提供!也就是谁提供下面的Bean对象,就提供这个配置类

  • 也就是"CommonConfig"这个配置类,要让第三方jar包提供
  • 然后还要提供应该自动配置类"CommomAutoConfig",并且在这个自动配置类上添加两个注解。分别是@AutoConfiguration它用来标识这个类是一个自动配置类、通过@Import注解把这个配置类"CommonConfig"导入进来,然后需要提供一个配置文件".imports",在这个配置文件中,把这个"CommomAutoConfig"自动配置类的全类名给它写入,具体图片操作如下展示:

  • 开始演示如何实现自动配置操作
  • 首先安装好对应的jar包,里面的代码已经基本写好

  • 接着安装到Maven的本地仓库
  • 接着在pom文件中手动的添加对应的依赖(common-pojo)完毕之后记得刷新Maven

  • 然后去查看对应库里安装的jar包(2.0版本的)

  • 继续查看里面的内容(配置类、自动配置类)
  • 自动配置类里用到两个注解@AutoConfiguration(标识当前是自动配置类)、@Import(把我们的"CommonConfig"这个配置类导入进来,也就是当springboot读取到这个自动配置类的时候,它就会读取到注解@Import,从而间接的读取到"CommonConfig"这个配置类)


  • 仿照之前在源码看到的——>在对应的目录下提供了一个配置文件"xxx.imports"

  • 在".imports"文件当中写了自动配置类的对应的全类名

  • 回到启动类,是否能够测试成功??打印getBean("province"),看是是否"Province"类的Bean对象已经能够自动的注入成功了。成功了!

  • 现在可以把启动类的@Import去掉了,因为能够自动配置了

六、分析总结

(1)小总结
  • 通过源码分析,可以看到springboot自动配置无非就是提供一个自动配置类,然后把这个类名写到指定的配置文件里边。
(2)面试(总结)

  • 25
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

岁岁岁平安

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值