Spring笔记--@ConditionalOnBean坑
@ConditionalOnBean 巨坑
场景:SpringBoot 引入 redis-starter , 加载 RabbitAutoConfiguration ,进而存在 StringRedisTemplate 。也可能排除掉 RabbitAutoConfiguration 。 自动义Bean,依赖 StringRedisTemplate。
字面理解太单纯,实际执行太复杂。
实际是: 执行到该注解时,如果已经存在某个类型的Bean,才创建当前Bean。否则不创建当前Bean
问题是: 在执行到该注解时,依赖的Bean还没有创建(它实际是要创建的,只是我们无法控制它的顺序), 它的顺序无法提前,无法被感知。
核心解决的问题: 1. 手动创建Bean。 2. 选择合适的时机创建Bean。
手动创建Bean
val registry: BeanDefinitionRegistry
get() {
if (registryField == null) {
throw RuntimeException("需要@Import(SpringUtil::class)")
}
return registryField!!
}
/**
* 动态注册Bean
*/
@JvmStatic
inline fun <reified T> registerBeanDefinition(
name: String,
instance: T,
callback: ((BeanDefinitionBuilder) -> Unit) = {}
) {
registry.registerBeanDefinition(name, getGenericBeanDefinition(instance, callback));
}
/**
* 动态创建Bean
*/
inline fun <reified T> getGenericBeanDefinition(
instance: T,
callback: ((BeanDefinitionBuilder) -> Unit) = {}
): GenericBeanDefinition {
val builder = BeanDefinitionBuilder.genericBeanDefinition(T::class.java);
callback(builder);
val definition = builder.rawBeanDefinition as GenericBeanDefinition;
definition.autowireMode = GenericBeanDefinition.AUTOWIRE_BY_TYPE
definition.instanceSupplier = Supplier { instance }
return definition;
}
时机1,利用 BeanPostProcessor
在初始化 StringRedisTemplate 时注册。
@Component
@Import(SpringUtil::class)
class StringRedisTemplateBeanProcessor : BeanPostProcessor {
override fun postProcessAfterInitialization(bean: Any, beanName: String): Any? {
if (bean.javaClass == StringRedisTemplate::class.java) {
var stringRedisTemplate = bean as StringRedisTemplate
stringRedisTemplate.hashValueSerializer = RedisSerializer.json()
SpringUtil.registerBeanDefinition("redisCacheDbDynamicService", RedisCacheDbDynamicService())
SpringUtil.registerBeanDefinition("redisRenewalDynamicService", RedisRenewalDynamicService())
}
return super.postProcessAfterInitialization(bean, beanName)
}
}
时机2,利用 ApplicationPreparedEvent 事件
类似逻辑,在所有Bean准备完之后,判断是否有依赖的Bean,再注册。
@Component
@Import(SpringUtil::class)
@ConditionalOnClass(MysqlDataSource::class)
class MySqlDataSourceConfig {
companion object {
@JvmStatic
val hasSlave: Boolean
get() {
return SpringUtil.containsBean("slave", DataSource::class.java);
}
}
@EventListener
fun prepared(ev: ApplicationPreparedEvent) {
if (SpringUtil.context.environment.getProperty("spring.datasource.url").isNullOrEmpty() &&
SpringUtil.context.environment.getProperty("spring.datasource.hikari.jdbc-url").isNullOrEmpty()
) {
return;
}
if (SpringUtil.containsBean(DataSourceAutoConfiguration::class.java) == false) {
return;
}
SpringUtil.beanFactory.getBeanDefinition("dataSource").isPrimary = true;
SpringUtil.beanFactory.getBeanDefinition("jdbcTemplate").isPrimary = true;
var slaveDataProperties =
SpringUtil.binder.bindOrCreate("spring.datasource-slave", DataSourceProperties::class.java);
if (slaveDataProperties.url.HasValue) {
var dataSourceSlave = slaveDataProperties.getDataSource()
SpringUtil.registerBeanDefinition("slaveDataSource", dataSourceSlave)
SpringUtil.registerBeanDefinition("slaveJdbcTemplate", JdbcTemplate(dataSourceSlave, true))
}
}
private fun DataSourceProperties.getDataSource(): HikariDataSource {
return this.initializeDataSourceBuilder().type(HikariDataSource::class.java)
.build() as HikariDataSource
}
}
![]() |
作者:NewSea 出处:http://newsea.cnblogs.com/
QQ,MSN:iamnewsea@hotmail.com 如无特别标记说明,均为NewSea原创,版权私有,翻载必纠。欢迎交流,转载,但要在页面明显位置给出原文连接。谢谢。 |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· 展开说说关于C#中ORM框架的用法!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
2018-09-15 生成字符画网站