问题引出
最近实习发现公司项目中有spring.factories文件,是用来定义starter的,让这个模块可以被其他模块引入pom依赖后直接使用,那为什么还必须用spring.factories配置才能被其他模块使用,直接引入依赖不能直接使用吗?
答案是能用,但是只能使用这个模块中的非Bean,也就是非spring容器中的类(例如工具类的静态方法)。这个模块定义的spring Bean不能用,会报找不到该Bean的错误,也就是扫描不到该模块中定义的Bean,比如下面例子:
在demo模块中直接使用starter模块的server1和server2两个bean,会报错:
为什么扫描不到?
SpringBoot默认扫包规则:在不自己定义scanBasePackage参数或scanBasePackageClasses参数或@ComponentScan扫包规则下,SpringBoot默认约定只会默认扫描主程序包下及其子包下的bean。
所以,你通过pom引入的第三方依赖,人家定义的包结构大概率和你的包结构不一样(包结构一般都是公司内部自定义的)。所以,依赖引入来的Bean就扫描不到!自然也就找不到该Bean。
使用@ComponentScan注解把所有包都列举出来,如果引入的依赖多了,显然不太现实。
那怎么办呢?怎么定义一个让别人直接能使用的stater呢?
starter怎么写
参考spring-boot-autoconfigure这个包的规则(其实这个包加上@SpringBootApplication注解就是springboot的自动装配原理):
1. 添加spring-boot-stater依赖(因为这个依赖里有spring-boot-autoconfigure这个依赖):
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
2.定义configuration类,在里面创建需要的bean:
3.在resource目录下新建目录META-INF/spring,并添加org.springframework.boot.autoconfigure.AutoConfiguration.imports文件:
文件内容就是我们的的配置类:
然后就可以直接将该依赖给其它项目使用。
如果需要像springboot官方的starter一样,导入一个starter之后在yml中写一些配置就能直接改变bean的属性值(yml中的配置其实就是一个个的bean对象,你改变配置其实就是在改bean的属性值),需要使用@ConfigurationProperties将bean和和配置文件绑定起来,再在configuration类上添加
@EnableConfigurationProperties注解。就能做到改yml配置文件就能修改bean的属性值,例如:
其实spring.factories文件这种做法其实在springboot3之后官方已经不再推荐使用,而是推荐使用
org.springframework.boot.autoconfigure.AutoConfiguration.imports
文件这种方式。
为什么不推荐使用spring.factories了
把作者的话翻译过来就是:
使用spring.factories
查找自动配置类会给我们的本地工作带来问题。我们最初使用它是因为代码已经可用,但我们希望在2.7中提供一个替代方案,并在3.0中停止使用spring.factories进行自动配置。
这句话中的navite是关键,它并不是本地的意思。而是cloud native中的native。一切为了云原生!
springboot3中一个很大的特色就是增加了很多对云原生的支持!
我们知道,云原生时代中,最重要的是什么?启动速度。那Java如何提升启动速度?AOT编译+云原生镜像。可是,传统方法使用spring.factories依赖于运行时扫描和加载自动配置类,这么做效率肯定不高。不适合云原生!
相比之下,使用org.springframework.boot.autoconfigure.AutoConfiguration.imports这种方式,允许在编译时确定自动配置类,减少了运行时开销,并使得像GraalVM这样的工具更容易分析和编译Spring Boot应用到原生映像。这种方法可以带来更快的启动时间和更低的内存消耗,这对于从可扩展性和效率受益的云原生应用至关重要。
SpringBoot自动配置原理
每个spring-boot-starter-xxx能包含spring-boot-sarter,而spring-boot-starter中又包含spring-boot-autoconfigure。spring-boot-configure中包含了所有所有场景的配置类,es的、redis的、jdbc的......等等等:
每个自动配置类大概都长这样:
并且该模块中同样有org.springframework.boot.autoconfigure.AutoConfiguration.imports文件:
在该文件中把所有的xxxAutoConfiguration全都写进来了:
也就是说,只要这个包下的所有类都能生效,那么相当于SpringBoot官方写好的整合功能就生效了。但是SpringBoot默认却扫描不到 spring-boot-autoconfigure
下写好的所有配置类,默认只扫描主程序所在的包。
然后就看主程序的@SpringBootApplication注解了:
这个注解由三个注解组成@SpringBootConfiguration
、@EnableAutoConfiguratio
、@ComponentScan。@SpringBootConfiguration和@Configuration功能差不多,都是将类定义为配置类,@ComponentScan注解默认扫
主程序所在的包的。关键就是@EnableAutoConfiguratio:
并且这个类中又使用了@Import导入了AutoConfigurationImportSelector类,这个类就是自动导入org.springframework.boot.autoconfigure.AutoConfiguration.imports文件中的配置类:
(在springboot3之前是spring.factories:)
至此,springboot就能把非主程序包的配置类全加载进来了。但是其实并不是全部都加载进来,而是按需加载!原因就在于每个配置类上都加了@ConditionOnxxx之类的注解,只有在这个条件成立的时候才会加载,例如:
拿@ConditionOnClass(JakartaWebServlet.class)举例,只有JakartaWebServlet存在时也就是这个依赖被导入时才加载。也就实现了你导入哪个依赖就加载哪个配置类,而不是全部都加载进来!
标签:怎么,SpringBoot,spring,配置,boot,autoconfigure,stater,bean,factories From: https://blog.csdn.net/m0_73520938/article/details/143377715