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  registerBeanDefinition(
    name: String,
    instance: T,
    callback: ((BeanDefinitionBuilder) -> Unit) = {}
) {
    registry.registerBeanDefinition(name, getGenericBeanDefinition(instance, callback));
}

/**
 * 动态创建Bean
 */
inline fun  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
    }
}

Spring笔记--@ConditionalOnBean坑

作者:
NewSea

出处:
http://newsea.cnblogs.com/

QQ,MSN:
iamnewsea@hotmail.com

如无特别标记说明,均为NewSea原创,版权私有,翻载必纠。欢迎交流,转载,但要在页面明显位置给出原文连接。谢谢。

Original: https://www.cnblogs.com/newsea/p/15271435.html
Author: NewSea
Title: Spring笔记–@ConditionalOnBean坑

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/541492/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • nacos单机,集群安装部署

    nacos单机启动 准备 下载nacos安装包 准备centos环境 (本次测试使用docker) PS C:\Users\Administrator> docker run…

    Java 2023年6月8日
    090
  • SpringBoot 系列 web 篇之自定义返回 Http Code 的 n 种姿势

    虽然 http 的提供了一整套完整、定义明确的状态码,但实际的业务支持中,后端并不总会遵守这套规则,更多的是在返回结果中,加一个 code 字段来自定义业务状态,即便是后端 5xx…

    Java 2023年5月30日
    074
  • 【Java全栈进阶】-继承

    抽象类的案例代码: //研发部员工 abstract class Developer{ public abstract void work(); //抽象函数,需要abstract…

    Java 2023年6月7日
    074
  • 通俗易懂的了解String中的intern方法

    首先,先看一下intern 方法(JDK1.8 )的官方文档: 全是英文,阅读起来有点困难怎么办?没关系,博主对此做了翻译: 返回字符串对象的规范表示形式。 最初为空的字符串池由类…

    Java 2023年6月16日
    075
  • java线程池

    Java中的线程池是运用场景最多的并发框架,几乎所有需要异步或并发执行任务的程序都可以使用线程池。在开发过程中,合理地使用线程池能够带来许多好处。 什么是线程池? Java中的线程…

    Java 2023年6月13日
    076
  • 从理论到实践,刨根问底探索Java对象内存布局

    从理论到实践,刨根问底探索Java对象内存布局 所谓对象的内存布局,就是对象在分配到内存中后的存储格式。 对象在内存中的布局一共包含三部分: 对象头(Header) 实例数据(In…

    Java 2023年6月5日
    078
  • nginx 学习,详细总结

    一 、nginx介绍 1.简介 Nginx 是高性能的 HTTP 和反向代理的web服务器,处理高并发能力是十分强大的,能经受高负 载的考验,有报告表明能支持高达 50,000 个…

    Java 2023年6月8日
    081
  • 复杂类型注入

    java;gutter:true; /</p> <pre><code> ;gutter:true;/java;gutter:true; valu…

    Java 2023年6月13日
    069
  • ArrayList源码分析

    源码分析 public ArrayList() {. //&#x9ED8;&#x8BA4;&#x6784;&#x9020;&#x51FD;&…

    Java 2023年6月8日
    0134
  • 2、内置注解

    @Ove rri d e 定义iava.lan .Override 中此注释只适用于修辞万法《表示一个方法声明打算 重写超类中的另一个方氵去声明. @Deprecated : 定义…

    Java 2023年6月8日
    092
  • 20220724-Java的封装相关

    含义 常见使用方法 个人理解 含义 封装 (encapsulation)指隐藏对象的属性和实现细节,仅对外公开接口,控制在程序中属性的读取和修改的访问级别。 常见使用方法 clas…

    Java 2023年6月15日
    0119
  • Java基础 try…catch(多个异常) 多个异常采取同样的解决措施

    JDK :OpenJDK-11 OS :CentOS 7.6.1810 IDE :Eclipse 2019‑03 typesetting :Markdown code packag…

    Java 2023年5月29日
    070
  • Maven配置私有仓库

    前言 当公司或个人具有自己独有的jar时,不想公开,一般就会放在自己的私有Maven仓库中,在项目中需要引用,此时就需要将公司私有仓库配置到maven当中,一般我们的maven配置…

    Java 2023年6月8日
    086
  • 分享自研实现的多数据源(支持同DB不同表、跨DB表、内存数据、外部系统数据等)分页查询工具类实现原理及使用

    思考: 提起分页查询,想必任何一个开发人员(不论是新手还是老手)都能快速编码实现,实现原理再简单不过,无非就是写一条SELECT查询的SQL语句,ORDER BY分页排序的字段, …

    Java 2023年6月9日
    096
  • Java SE 多态

    多态数组:数组的定义类型为父类类型,里面保存的实际元素类型为子类类型 public class PloyArray { public static void main(String…

    Java 2023年6月7日
    0189
  • 购买新电脑后必做的几件事(操作系统为Windows)

    新买的电脑,无论是台式机,还是笔记本,买来之后,通常都有一些事情,是必须要做的,特别是 Windows 电脑。 以下详细描述: 1. 连上互联网,更新 Wiindows 系统。 主…

    Java 2023年6月9日
    093
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球