工作纪实_21-搭建脚手架对spring注解bean顺序管理引发的思考

最近在给公司搭建一套脚手架,其中有对一些通用功能,比如shiro授权模块的代码,以及网关解析token的代码做一些封装,发现对于spring的bean关系管理,存在一些值得记录的问题

场景 1.A类代码中,依赖于B类中注入的一个bean

如果搭建过项目的工具包,封装过一些功能的同仁肯定会遇见这个问题,举个小列子:

@Configuration
public class A{
	
	@Bean
	public B b() {
		return new B();
	}
}
@Configuration
public Class C{

	@Autowired
	private B b;
	
	@Bean D d() {
		return new D();
	}
}

很明显,C是依赖于A中注入的Bean【B】,那么启动项目时,我们如果无法保证A在C之前先初始化出bean的话,那么启动肯定会报C类中找不到存在的Bean【B】
因此,需要一些手段来管理Bean的这些加载、初始化的顺序!

解决方案

这里面分两种环境:

1.脚手架包

@AutoConfigureBefore @AutoConfigureAfter @AutoConfigureOrder

这里我们需要注意一个点就是,必须是含有spring.facotries文件,且当前模块不含有启动类,才可以使用,否则,是不会生效的,spring的加载SPI机制会根据我们的指示去加载。

@AutoConfigureOrder只能改变外部依赖的@Configuration的顺序。
如何理解是外部依赖呢?
能被你工程内部scan到的包,都是内部的Configuration,而spring引入外部的Configuration,都是通过spring特有的spi文件:spring.factories
总结:@AutoConfigureOrder能改变spring.factories中的@Configuration的顺序

2. 含有启动类的包

@Depeonds依赖加载

这就是直接写在启动类的一个包下的代码,我们可以使用@DepeondsOn("b")来保证启动时候,spring加载bean顺序,按照我们预期的方向去走。
@DependsOn注解可以用来控制bean的创建顺序,该注解用于声明当前bean依赖于另外一个bean。所依赖的bean会被容器确保在当前bean实例化之前被实例化

3. 简单方案【一般场景】

@Bean参数注入

示例:

@Bean
public D d(B b){
  return new D();
}

Spring在初始化@Bean标记的注解时,如果发现容器没有参数传入的bean,会自动的去初始化,但是这种不适用于跨类的注入,如果B是常规的bean还好,但是如果B的初始化也依赖很多其他的bean,这种方法使用起来,会比较麻烦一点

4.ObjectProvider
@Autowired
private ObjectProvider<UserDetailsService> objectProvider;

有兴趣的同学可以了解下,这个是后面我发现的一个好方法,某些场景下也比较好用,启动包的时候不会去检查依赖关系,只会在调用的时候,去尝试获取bean

5.SpringUtils.getBean(T.class)

某些情况下,我们并不是在启动项目的时候就需要立刻加载bean,而是在它启动之后,被调用时才加载,但是启动项目的时候,spring又会去检测依赖的bean,这个时候,我们可以不采用@Autowired去引入bean,而是在它被调用的时候,直接利用根据去获取,打个时间差,也可以避免这种问题!

使用误区

1. @Order注解来控制

可能最开始想到的就是这个注解,但是很遗憾,这个是无法控制bean的加载顺序的
通过@Order这个标注进行顺序的控制标注加在普通的方法上或者类上一点鸟用都没有
目前用的比较多的有以下3点:

  • 控制AOP的类的加载顺序,也就是被@Aspect标注的类
  • 控制ApplicationListener实现类的加载顺序
  • 控制CommandLineRunner实现类的加载顺序

2.@Condition簇注解

@ConditionalOnBean``@ConditionalOnClass… 来控制顺序
但是源代码中,对于这个簇的使用,有这么一句话:

The condition can only match the bean definitions that have been processed by the
application context so far and, as such, it is strongly recommended to use this
condition on auto-configuration classes only. If a candidate bean may be created by
another auto-configuration, make sure that the one using this condition runs after.
百度翻译:
条件只能匹配已由处理的bean定义!到目前为止的应用程序上下文,因此,强烈建议使用此条件仅适用于自动配置类。如果候选bean可以由另一个自动配置,请确保使用此条件的配置在之后运行。

这也就是说,使用这一类注解时,Spring是根据当时加载bean的实际情况来判断是否加载bean的,这个也没有办法保证bean的加载顺序,且推荐使用方式为:适用于自动配置类!
总结:在确切知道bean的加载有先后顺序的情况下,使用@ConditionalOnBean类型的注解,才会有效!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值