自定义bean加载到spring容器中的两种方式:
- 1.在类上添加注解
@Controller
、@RestController
(本质是@Controller
)、@Service
、@Repository
、@Component
- 2.使用
@Configuration
和@Bean
这篇文章主要介绍第二种方式原理(因为在实际使用中,第二种方式遇到了一个问题,记录一下)。
抛出问题
在微服务中定义一个文件上传模块,目的是为了以后在其他任何需要的微服务中直接引用。由于配置是在配置文件中,需要使用@Value("${xxx}")
获取配置(VolcengineUtils.java
中获取),故需要被spring
注解扫描到,并初始化上传对象,但是始终没有初始化这个配置文件!!!
@Configuration
public class VolcengineConfig {
@Value("${meta.volcengine.endPoint}")
private String endPoint;
@Value("${meta.volcengine.region}")
private String region;
@Value("${meta.volcengine.accessKey}")
private String accessKey;
@Value("${meta.volcengine.secretKey}")
private String secretKey;
@Value("${meta.volcengine.bucketName}")
private String bucketName;
@Bean
public void initVolcengineInfo(){
VolcengineUtils.setClient(new TOSV2ClientBuilder().build(region, endPoint, accessKey, secretKey));
VolcengineUtils.setBucketName(bucketName);
}
}
思考1:直接把配置文件放到项目中没有问题,移出作为一个模块怎么就会有问题?
对比后发现:服务项目和上传组件的包(package
)的名称不一样
服务项目:
上传组件:
然后修改上传组件的包名为:com.meta.modules.file.volcengine
再次启动:没问题了!
思考2.为什么修改包名相同就可以了?那如果对外提供组件,总不能要求别人的服务项目和组件同包名吧?
于是继续寻找解决方案:然后找到在resources
资源目录下META-INF\spring.factories
中增加扫描路径可以被spring
加载,于是增加META-INF\spring.factories
文件(上传组件包名已还原为:com.meta.core.file.volcengine
)
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.meta.core.file.volcengine.VolcengineConfig
再次启动:没问题了!
回答思考1不同包名不能被扫描问题:
1.服务启动项:点击注解@SpringBootApplication
进入
2.点击注解@ComponentScan
进入
注释翻译:如果指定的包没有被定义,则扫描将从声明该注释的类的包中进行。
也就是如果没有配置@ComponentScan
注解中basePackageClasses
、basePackages
、value
的值,则从@SpringBootApplication
注解类所在的包开始扫描
思考3.spring.factories
配置是如何加载的?
1.服务启动项:点击注解@SpringBootApplication
进入–>点击注解@EnableAutoConfiguration
进入
发现有一个自动配置导入选择器,点击进入AutoConfigurationImportSelector.class
类中
通过断点即可发现:在spring.factories
中指定的配置已经被加载到spring容器中。
优化VolcengineConfig类: 使用spring.factories
加载配置显然更通用,其次此处只需要初始化一次配置,不需要把initVolcengineInfo
声明为bean
。使用@PostConstruct
在bean加载完成后初始化一次即可
public class VolcengineConfig {
@Value("${meta.volcengine.endPoint}")
private String endPoint;
@Value("${meta.volcengine.region}")
private String region;
@Value("${meta.volcengine.accessKey}")
private String accessKey;
@Value("${meta.volcengine.secretKey}")
private String secretKey;
@Value("${meta.volcengine.bucketName}")
private String bucketName;
@PostConstruct
public void initVolcengineInfo(){
VolcengineUtils.setClient(new TOSV2ClientBuilder().build(region, endPoint, accessKey, secretKey));
VolcengineUtils.setBucketName(bucketName);
}
}