目录
一、引言
- 之前学习了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对象已经能够自动的注入成功了。成功了!
- 发现运行程序,输出成功!就证明我们已经达到自动配置的效果了!!实在看不懂区别可以查看我昨天写的博客文章。这个地方确实难理解,本人也同样!!!SpringBoot学习(8)(Bean注册条件)(@Conditional的衍生注解)-CSDN博客文章浏览阅读627次,点赞8次,收藏6次。主要是关于springboot中Bean对象的注册条件的合理使用时机、方法。其中演示了如何注册条件将第三方jar包中的对应实体类的Bean对象注入到loc容器当中。注解包括@Bean,@Conditional及其衍生注解@ConditionalOnProperty、@Conditional0nMissingBean、@ConditionalOnclass。https://blog.csdn.net/m0_74363339/article/details/141958063?spm=1001.2014.3001.5501
- 现在可以把启动类的@Import去掉了,因为能够自动配置了
标签:面试题,这个,SpringBoot,配置文件,配置,Bean,源码,自动,注解 From: https://blog.csdn.net/m0_74363339/article/details/141998533六、分析总结
(1)小总结
- 通过源码分析,可以看到springboot自动配置无非就是提供一个自动配置类,然后把这个类名写到指定的配置文件里边。
(2)面试(总结)