惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

V
Vulnerabilities – Threatpost
U
Unit 42
F
Fortinet All Blogs
aimingoo的专栏
aimingoo的专栏
P
Proofpoint News Feed
F
Full Disclosure
月光博客
月光博客
Engineering at Meta
Engineering at Meta
博客园_首页
The Register - Security
The Register - Security
G
Google Developers Blog
The Cloudflare Blog
博客园 - Franky
K
Kaspersky official blog
A
Arctic Wolf
Scott Helme
Scott Helme
C
Cisco Blogs
Hugging Face - Blog
Hugging Face - Blog
C
Check Point Blog
NISL@THU
NISL@THU
AI
AI
D
DataBreaches.Net
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Stack Overflow Blog
Stack Overflow Blog
Project Zero
Project Zero
The GitHub Blog
The GitHub Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
量子位
Vercel News
Vercel News
T
Tor Project blog
P
Privacy International News Feed
D
Docker
I
Intezer
L
LangChain Blog
P
Proofpoint News Feed
Security Latest
Security Latest
C
CXSECURITY Database RSS Feed - CXSecurity.com
T
Threatpost
博客园 - 聂微东
AWS News Blog
AWS News Blog
Martin Fowler
Martin Fowler
P
Privacy & Cybersecurity Law Blog
V
V2EX
Last Week in AI
Last Week in AI
C
Cybersecurity and Infrastructure Security Agency CISA
The Hacker News
The Hacker News
T
Tenable Blog
Blog — PlanetScale
Blog — PlanetScale
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
T
Tailwind CSS Blog

博客园 - NewSea

Linux 安装IntelAx211无线网卡 解决 K8sApi 部署后报 Unknown apiVersionKind apps/v1/Deployment is it registered? Docker 启动前后端脚本 docker 一键启动 mariadb 分区脚本 K8s笔记 ubuntu18+k8s单机版+kuboard+harbor安装笔记 yapi 自定义Json的数据类型 Nginx笔记 k3s+rancher+harbor 笔记 postgresql 笔记 vscode 笔记 发布Jar包到中央仓库 Java开发笔记汇总 - NewSea - 博客园 css 适配 nui-app 笔记 Java里的不能与无用. SSH 配置 Swagger 配置
Spring笔记--@ConditionalOnBean坑
NewSea · 2021-09-15 · via 博客园 - NewSea

@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
    }
}