SpringBoot 开发总结思考(二)
1、SpringBoot 自动配置(装配)
.原理是什么?
https://blog.csdn.net/u014745069/article/details/83820511
.为什么要有?
2、SpringBoot 是如何把一些库加入到IOC中的?
@Component、@Configuration 适用于自定义业务,而SpringBoot考虑的是如何把第三方以及自己的库加入到IOC中
@SpringBootApplication 就是一个超级大的配置类,其中包含的注解会分解小的配置类模块
3、@EnableAutoConfiguration :SpringBoot中最核心的注解
主要作用就是为了加载N多的Bean,然后将其放入IOC中
部分的属性是写在配置文件中的,允许从配置文件中更改配置的Bean的属性等
必须要能把Bean加入到IOC中
@Import(AutoConfigurationImportSlector.class) AutoConfigurationImportSlector 中有一个非常重要的方法:selectImports
.核心方法,用于加载第三方配置类
要加载的所有的配置都在spring.factories中记录,SpringBoot 会读取该文件所有的Bean,然后再将这些Bean加入到IOC中
spring.factories 中的配置类与SpringBoot的自动加载机制是如何关联起来的?
.核心就是 .factories 配置文件
4、第三方的Bean也加了 @Configuration,为什么还要通过 @EnabledAutoConfiguration + .factories 的机制加入到IOC中?
如果只是加了 @Configuration ,相当于直接把源码加入到了IOC中
第三方SDK也直接将源码加入到IOC中吗?显然不可能
5、以Enabled 开头的注解,被称之为模块装配
模块装配:假设要注入MongoDB,那么就加上@Configuration注解,有可能一个配置类没办法解决某个方向的问题,往往是很多@Configuration的类组合在一起
SpringBoot是使用Enable注解,然后再通过@import导入Selector,通过Selector读取 .factories 文件,最终加载的Configuration
6、@EnableAutoConfiguration 最终还是要加载@Configuration
比起自定义@Configuration ,还多了一步@Import的Selector
修改前面的 HeroConfiguration 类,并将所有的@Component去掉
@Configuration public class HeroConfiguration { //@Bean public ISkill diana(){ return new Diana("Diana", 1); } @Bean public ISkill irelia(){ return new Irelia(); } }
但现在的结构仍然可以自动将HeroConfiguration 加载到IOC中,因为启动类的@SpringBootApplication包含了ComponentScan,当前包下的标注了@Component/@Configuration的都会被加入到IOC中
自定义一个 启动类 LOLApplication,并将启动类的@SpringBootApplication注解去掉
@ComponentScan public class LolApplication { public static void main(String[] args) { ConfigurableApplicationContext context = new SpringApplicationBuilder(LolApplication.class) .run(args); ISkill iSkill = (ISkill) context.getBean("irelia"); iSkill.r(); } }
使用另一种@Import方式
.最简单的一种方式就是,将配置类传入到Import中
@Import(HeroConfiguration.class)
此时会报错,unable to start web service,为什么加@ComponentScan就可以?
在默认情况下,上下文会启动Web服务器,如果不主动关闭Web服务器的模式,它就会自动启动
之所以加@ComponentScan可以,是因为该注解会扫描和web服务器相关配置的
但注释之后使用@Import只是导入了一个配置类,所以很多SpringBoot内置的配置都是没有导入到IOC中
增加WebApplicationType.NONE 把服务器关闭
@Import(HeroConfiguration.class) public class LolApplication { public static void main(String[] args) { ConfigurableApplicationContext context = new SpringApplicationBuilder(LolApplication.class) .web(WebApplicationType.NONE) .run(args); ISkill iSkill = (ISkill) context.getBean("irelia"); iSkill.r(); } }
@Import 第二个用法
- 传入的参数增加一个Selector
新增一个Selector:LolConfigurationSelector
public class LolConfigurationSelector implements ImportSelector { /** * 该接口要求返回一个数组,这个数组是一个字符串类型 * @param annotationMetadata * @return */ @Override public String[] selectImports(AnnotationMetadata annotationMetadata) { /** * 其实还是在返回元类的名字,一次可以导入多个配置类 */ return new String[] {HeroConfiguration.class.getName()}; } }
更改启动类 LolApplication
@Import(LolConfigurationSelector.class) public class LolApplication { public static void main(String[] args) { ConfigurableApplicationContext context = new SpringApplicationBuilder(LolApplication.class) .web(WebApplicationType.NONE) .run(args); ISkill iSkill = (ISkill) context.getBean("irelia"); iSkill.r(); } }
7、SpringBoot自动装配的spring.factories文件
新建一个Enable 注解类:EnableLolConfiguration(相当于一个模块装配)
元注解需要标注一些注解
@Retention(RetentionPolicy.RUNTIME) :用来修饰注解,是注解的注解
@Target(ElementType.TYPE) : 被描述的注解可以用在什么地方
@Documented : 表明这个注释是由 javadoc记录的
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented @Import(LolConfigurationSelector.class) public @interface EnableLolConfiguration { }
修改 LolApplication
@EnableLolConfiguration public class LolApplication { public static void main(String[] args) { ConfigurableApplicationContext context = new SpringApplicationBuilder(LolApplication.class) .web(WebApplicationType.NONE) .run(args); ISkill iSkill = (ISkill) context.getBean("irelia"); iSkill.r(); } }
8、@EnableAutoConfiguration 为什么要从 .factories 文件读取?
一个模块具体要加载哪些配置类,是一个变化的过程。
既然是变化,那么就应该被隔离到配置文件中
但不是所有在 .factories 文件中的配置都会被导入,所以这些配置文件中的类被称为 候选配置类
满足一定条件之后,配置类才会被导入到IOC中
AutoConfigurationImportSelector中有一个很重要的方法就是:getCandidateConfigurations(获取所有的候选配置类名)
9、Java SPI 机制解析
SpringBoot自动装配机制其实是SPI机制/思想的应用
SPI 机制/思想:
. Service Provider Interface (服务发现机制)
SPI 就是应对变化,一个系统中往往会依赖很多模块,这些模块又是有各种各样不同实现方案的,有时候需要灵活的替换实现方案
首先有调用方,再有标准服务接口,服务接口下可能会有很多不同的实现(方案A、方案B),不同的方案遵循标准的服务接口,就可以被调用方调用;换句话说就是在Java中SPI,等同于基于接口编程。不基于Interface接口编程,无法提供标准服务接口,也无法将众多方案的具体隐藏在抽象之后
SpringBoot自动加载机制就是SPI机制/思想的衍生
对于一个方案来说,完全可以通过修改 .factories文件去切换不同的配置类,切换不同配置类的实质就是在切换众多的方案之一
10、再谈如何解决变化?
既然有了@Primary、条件注解之后,为什么还要有SPI机制/思想?
首先肯定的是,无论是SPI 还是@Primary、条件注解机制,都是为了解决变化而生
但是两者关注模块的细粒度是不一样的
.@Primary、条件注解 关注的是类/对象层面的变化,关注的具体/粒度非常小
.SPI 关注的点非常抽象,是整体解决方案的切换;有时候通过简单的类替换是没办法解决问题的
举个栗子
.装修的时候将1木质地板换成大理石地板(@Primary、条件注解)
.房子的整体装修风格发生了变化(SPI)
两者应对变化没有明显的界限
11、SpringBoot自动装配到底做了一件什么事情?
SpringBoot 较其他框架多出了一个IOC容器,SpringBoot自动装配真正解决了如何发现这些Bean并把这些配置的Bean要能够加入到IOC容器中,如果没有IOC,则需要手动在XML指定