zoukankan      html  css  js  c++  java
  • 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
        }
    }
    
    alarm   作者:NewSea     出处:http://newsea.cnblogs.com/    QQ,MSN:iamnewsea@hotmail.com

      如无特别标记说明,均为NewSea原创,版权私有,翻载必纠。欢迎交流,转载,但要在页面明显位置给出原文连接。谢谢。
  • 相关阅读:
    实现一个文件系统
    ICN开发指导
    GPU in container
    docker debug
    内核代码中一些c语言用法
    各种Tree的python 实现
    intel VT-X (VMX) spec 解读
    intel VT-D (iommu) spec 解读
    正交幅度调制QAM
    Load balancer does not have available server for client:xxx
  • 原文地址:https://www.cnblogs.com/newsea/p/15271435.html
Copyright © 2011-2022 走看看