SpringBoot中的bean加载顺序
本文内容纲要:
-
-
-https://www.dazhuanlan.com/2019/10/22/5daebc5d16429/
-@Order注解
-@AutoConfigureAfter注解
-@DependsOn注解
-属性注入和构造器注入
有几个点需要去琢磨透的:
1、spring.factrores中的configuration跟项目里面自身的configuration一样么(在容器中存储的地方一样么)?
2、spring扫描的顺序会因为什么发生改变?默认是什么样子的顺序,文件加载拍序(跟当前应用所处的系统相关)
3、@Import、factoryBean、@Component、@Bean之间的顺序加载?
4、还有一个平台的platform-hibernate需要去进一步定位问题
https://www.dazhuanlan.com/2019/10/22/5daebc5d16429/
最近在做传统Spring项目到SpringBoot项目迁移过程中,遇到了一些bean加载顺序的问题:
比如一个config中的bean依赖于另一个config中的bean进行初始化,于是查了一些资料,出现了一些新的概念:
@Order
@AutoConfigureAfter
@DependsOn
@Order
注解
BeforeSpring4.0,the
@Order
annotationwasusedonlyfortheAspectJ
executionorder.Itmeansthehighestorderadvicewillrunfirst.SinceSpring4.0,itsupportstheorderingofinjectedcomponentstoacollection.Asaresult,Springwillinjecttheauto-wiredbeansofthesametypebasedontheirordervalue.
在Spring4.0版本之前,@Order
注解只能控制AOP的执行顺序,在Spring4.0之后,它还可以控制集合注入中bean的顺序。
控制AOP顺序很好理解,例如可以在@Aspect
注解的切面上加入@Order
注解,控制切面的执行顺序。
还有@EnableTransactionManagement(order=10)
,这种写法,由于Spring的事务也是用AOP实现,也可以控制优先级。
下面举个例子说明控制集合注入中bean的顺序。
publicinterface{
intgetRating();
}
@Order(1)
publicclassExcellentimplements{
@Override
publicintgetRating(){
return1;
}
}
@Order(2)
publicclassGoodimplements{
@Override
publicintgetRating(){
return2;
}
}
@Order(Ordered.LOWEST_PRECEDENCE)
publicclassAverageimplements{
@Override
publicintgetRating(){
return3;
}
}
最后是测试类:
publicclassRatingRetrieverUnitTest{
@Autowired
privateList<Rating>ratings;
@Test
publicvoidgivenOrder_whenInjected_thenByOrderValue(){
assertThat(ratings.get(0).getRating(),is(equalTo(1)));
assertThat(ratings.get(1).getRating(),is(equalTo(2)));
assertThat(ratings.get(2).getRating(),is(equalTo(3)));
}
}
如果不使用@Order
注解,那ratings集合可能是乱序的。
有一种错误的用法:
先定义两个service:
@Service
publicclassOrderService1{
privatestaticfinalLoggerLOGGER=LoggerFactory.getLogger(OrderService1.class);
publicOrderService1(){
LOGGER.info("OrderService1constructor");
}
publicStringname(){
return"orderService1";
}
}
@Service
publicclassOrderService2{
privatestaticfinalLoggerLOGGER=LoggerFactory.getLogger(OrderService2.class);
publicOrderService2(){
LOGGER.info("OrderService2constructor");
}
publicStringname(){
return"orderService2";
}
}
然后写两个config注入bean:
@Configuration
@Order(1)
publicclassOrderService1Config{
privatestaticfinalLoggerLOGGER=LoggerFactory.getLogger(OrderService1Config.class);
@Bean
publicOrderService1orderService1(){
LOGGER.info("orderService1init");
returnnewOrderService1();
}
}
@Configuration
@Order(0)
publicclassOrderService2Config{
privatestaticfinalLoggerLOGGER=LoggerFactory.getLogger(OrderService2Config.class);
@Bean
publicOrderService2orderService2(){
LOGGER.info("orderService2init");
returnnewOrderService2();
}
}
本意是想通过@Order
控制bean的注入顺序,先注入orderService2,再注入orderService1。但是并没有效果。
所以,@Order
注解放到@Configuration
中是无法控制bean的注入顺序的。
@AutoConfigureAfter
注解
Hintforthatan
EnableAutoConfiguration
auto-configurationshouldbeappliedafterotherspecifiedauto-configurationclasses.
类似的注解还有:
@AutoConfigureBefore
@AutoConfigureOrder
这三个注解是特地用于autoconfigure类的,不能用于普通的配置类。
有必要先说明一下autoconfigure类项目。
通常我们会在主类入口上标注@SpringBootApplication
注解,或者直接标注@EnableAutoConfiguration
注解。
这个注解是用来根据类路径中的依赖包猜测需要注入的bean,实现自动注入:
Enableauto-configurationoftheSpringApplicationContext,attemptingtoguessandconfigurebeansthatyouarelikelytoneed.Auto-configurationclassesareusuallyappliedbasedonyourclasspathandwhatbeansyouhavedefined.
可以理解为,@EnableAutoConfiguration
是服务于自动注入的bean的,即spring-boot-starter
中bean的自动加载顺序。
被排序的这些类,都是通过xxx-spring-boot-autoconfigure
项目中的src/resources/META-INF/spring.factories
配置文件获取的,这个文件中的配置内容一般为:
#AutoConfigure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=
com.github.pagehelper.autoconfigure.PageHelperAutoConfiguration
SpringBoot只会对从这个文件读取的配置类进行排序。
但是不要以为将自己的配置类也配置在spring.factories
中就能实现排序,如果你的类被自己SpringBoot
启动类扫描到了,这个类的顺序会优先于所有通过spring.factories
读取的配置类。
Auto-configurationisalwaysappliedafteruser-definedbeanshavebeenregistered.
@DependsOn
注解
Beansonwhichthecurrentbeandepends.Anybeansspecifiedareguaranteedtobecreatedbythecontainerbeforethisbean.
Usedinfrequentlyincaseswhereabeandoesnotexplicitlydependonanotherthroughpropertiesorconstructorarguments,butratherdependsonthesideeffectsofanotherbean’sinitialization.
Maybeusedonanyclassdirectlyorindirectlyannotatedwithorg.springframework.stereotype.ComponentoronmethodsannotatedwithBean.
UsingDependsOnattheclasslevelhasnoeffectunlesscomponent-scanningisbeingused.IfaDependsOn-annotatedclassisdeclaredviaXML,DependsOnannotationmetadataisignored,andisrespectedinstead.
从javadoc中可以看出,@DependsOn
注解可以用来控制bean的创建顺序,该注解用于声明当前bean依赖于另外一个bean。所依赖的bean会被容器确保在当前bean实例化之前被实例化。
一般用在一个bean没有通过属性或者构造函数参数显式依赖另外一个bean,但实际上会使用到那个bean或者那个bean产生的某些结果的情况。
用法
- 直接或者间接标注在带有
@Component
注解的类上面; - 直接或者间接标注在带有
@Bean
注解的方法上面; - 使用
@DependsOn
注解到类层面仅仅在使用component-scanning方式时才有效;如果带有@DependsOn
注解的类通过XML方式使用,该注解会被忽略,<beandepends-on="..."/>
这种方式会生效。
例如,我们有一个FileProcessor
依赖于FileReader
和FileWriter
,FileReader
和FileWriter
需要在FileProcessor
之前初始化:
@Configuration
@ComponentScan("com.baeldung.dependson")
publicclassConfig{
@Bean
@DependsOn({"fileReader","fileWriter"})
publicFileProcessorfileProcessor(){
returnnewFileProcessor();
}
@Bean("fileReader")
publicFileReaderfileReader(){
returnnewFileReader();
}
@Bean("fileWriter")
publicFileWriterfileWriter(){
returnnewFileWriter();
}
}
也可以在Component
上标注:
@DependsOn({"filereader","fileWriter"})
publicclassFileProcessor{}
属性注入和构造器注入
上面说到@DependsOn
注解时提到,它一般用在一个bean没有通过属性或者构造函数参数显式依赖另外一个bean,但实际上会使用到那个bean或者那个bean产生的某些结果的情况。
如果bean直接依赖于另一个bean,我们可以将其通过属性或者构造函数引入进来。
而使用构造函数的方法显示依赖一个bean,能够保证被依赖的bean先初始化。但是属性注入不可以。
constructor-injectionautomaticallyenforcestheorderandcompletenessoftheinstantiated.
因此,我们可以在Component中使用构造函数显示注入依赖的bean:
@Autowired
publicMyComponent(@Qualifier("jedisTemplateNew")JedisTemplatejedisTemplateNew){
}
注意,需要使用@Qualifier
限定bean名称时,不能标注在构造方法上,而是应该标注在参数上。原因跟@Resource
不能标注构造方法一样,它不知道你要限定哪个参数。
假设有两个service,OrderService1
依赖于OrderService2
:
publicclassOrderService1{
privatestaticfinalLoggerLOGGER=LoggerFactory.getLogger(OrderService1.class);
privateOrderService2orderService2;
publicOrderService1(OrderService2orderService2){
LOGGER.info("OrderService1constructor");
this.orderService2=orderService2;
}
publicStringname(){
Stringname=orderService2.name();
LOGGER.info("OrderService1printorderService2name={}",name);
return"orderService1";
}
}
publicclassOrderService2{
privatestaticfinalLoggerLOGGER=LoggerFactory.getLogger(OrderService2.class);
publicOrderService2(){
LOGGER.info("OrderService2constructor");
}
publicStringname(){
return"orderService2";
}
}
对应的Configuration:
@Configuration
publicclassOrderService1Config{
privatestaticfinalLoggerLOGGER=LoggerFactory.getLogger(OrderService1Config.class);
@Autowired
privateOrderService2ConfigorderService2Config;
@Bean
publicOrderService1orderService1(){
LOGGER.info("orderService1init");
returnnewOrderService1(orderService2Config.orderService2());
}
}
@Configuration
publicclassOrderService2Config{
privatestaticfinalLoggerLOGGER=LoggerFactory.getLogger(OrderService2Config.class);
@Bean
publicOrderService2orderService2(){
LOGGER.info("orderService2init");
returnnewOrderService2();
}
}
输出:
c.m.s.config.OrderService1Config:orderService1init
c.m.s.config.OrderService2Config:orderService2init
c.m.s.service.OrderService2:OrderService2constructor
c.m.s.service.OrderService1:OrderService1constructor
可以看出,OrderService2
先初始化。
换一种OrderService1
写法,使用属性注入:
publicclassOrderService1{
privatestaticfinalLoggerLOGGER=LoggerFactory.getLogger(OrderService1.class);
privateOrderService2orderService2;
publicvoidsetOrderService2(OrderService2orderService2){
this.orderService2=orderService2;
}
publicOrderService1(){
LOGGER.info("OrderService1constructor");
}
publicStringname(){
Stringname=orderService2.name();
LOGGER.info("OrderService1printorderService2name={}",name);
return"orderService1";
}
}
@Configuration
publicclassOrderService1Config{
privatestaticfinalLoggerLOGGER=LoggerFactory.getLogger(OrderService1Config.class);
@Autowired
privateOrderService2ConfigorderService2Config;
@Bean
publicOrderService1orderService1(){
LOGGER.info("orderService1init");
OrderService1orderService1=newOrderService1();
orderService1.setOrderService2(orderService2Config.orderService2());
returnorderService1;
}
}
输出:
c.m.s.config.OrderService1Config:orderService1init
c.m.s.service.OrderService1:OrderService1constructor
c.m.s.config.OrderService2Config:orderService2init
c.m.s.service.OrderService2:OrderService2constructor
可以看出,OrderService2
并没有先初始化。
当然,OrderService1Config
也可以使用构造器注入:
@Configuration
publicclassOrderService1Config{
privatestaticfinalLoggerLOGGER=LoggerFactory.getLogger(OrderService1Config.class);
privatefinalOrderService2ConfigorderService2Config;
@Autowired
publicOrderService1Config(OrderService2ConfigorderService2Config){
this.orderService2Config=orderService2Config;
}
@Bean
publicOrderService1orderService1(){
LOGGER.info("orderService1init");
OrderService1orderService1=newOrderService1();
orderService1.setOrderService2(orderService2Config.orderService2());
returnorderService1;
}
}
参考:
@OrderinSpring
ControllingBeanCreationOrderwith@DependsOnAnnotation
本文内容总结:,,https://www.dazhuanlan.com/2019/10/22/5daebc5d16429/,@Order注解,@AutoConfigureAfter注解,@DependsOn注解,属性注入和构造器注入,
原文链接:https://www.cnblogs.com/longxok/p/12021173.html