V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
beryl
V2EX  ›  程序员

Java 动态切换两个不同的类声明

  •  1
     
  •   beryl · 2019-05-26 12:29:11 +08:00 · 3306 次点击
    这是一个创建于 1769 天前的主题,其中的信息可能已经有所发展或是发生改变。

    使用:

    // case 1

    @Resource("testService")
    private RedisClient redisClient;
    
    
    // ...
    // 省略重写的 redis 方法
    
    

    // case 2

    @Resource("testService")
    private RedisClusterClient redisClient;
    
    
    // ...
    // 省略重写的 redis 方法
    
    

    期望统一成一个:

    @Resource("testService")
    private MyRedisClient redisClient;
    
    
    // ...
    // 省略重写的 redis 方法
    
    

    想根据不同配置,MyRedisClient 代理到不同的单例或者集群模式的类。

    因为不同环境有的是单点的,要使用 case1 的方式, 有的是 cluster 模式,要使用 case2, 但是这样要切换代码才可以。

    想通过配置方式不知道怎么实现,想到可能需要通过代理模式实现,但是具体细节一直没有搞清楚?

    Java 动态代理 工厂模式

    28 条回复    2019-05-27 13:58:03 +08:00
    undeflife
        1
    undeflife  
       2019-05-26 12:41:04 +08:00   ❤️ 2
    通过配置文件和 @Conditional 方式自动切换

    @Conditional(value = { RedisContextHolder.RedisServiceCondition.class })
    private RedisClient redisClient;

    @Conditional(value = { RedisClusterContextHolder.RedisClusterServiceCondition.class })
    private RedisClusterClient redisClient;



    public static class RedisServiceCondition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
    context.getEnvironment().getProperty("application.redis")
    ....
    return boolean;
    }
    }
    bxb100
        2
    bxb100  
       2019-05-26 12:41:40 +08:00
    通过启动参数然后 if else?
    beryl
        3
    beryl  
    OP
       2019-05-26 12:44:01 +08:00
    @bxb100 怎么在注解中实现这种呢?
    undeflife
        4
    undeflife  
       2019-05-26 12:44:03 +08:00
    上面写得不太对 Conditional 写在类上,这俩 Client 最好有实现相同的接口或父

    使用 redis 的地方自动注入即可
    beryl
        5
    beryl  
    OP
       2019-05-26 12:47:36 +08:00
    @undeflife 因为有个 client 是一个第三方库, 是个接口,rediscluster 是我自己写的。 不太清楚怎么抽象出来一个父类。

    另外 Conditional 方法还没用过,在查。每太看明白你上面的示例

    thx
    xiangyuecn
        6
    xiangyuecn  
       2019-05-26 12:52:41 +08:00
    interface MyRedisClient{
    定义需要的 redis 功能操作方法
    }

    根据单点或集群分别实现 MyRedisClient 底层调用逻辑
    RedisClient implements MyRedisClient
    RedisClusterClient implements MyRedisClient


    通过配置给 client 动态赋值
    private MyRedisClient redisClient=new (RedisClient or RedisClusterClient)


    别说我现在用的就是这么干的。。。不过我用 c#写的一套
    beryl
        7
    beryl  
    OP
       2019-05-26 12:53:56 +08:00
    @xiangyuecn 现在 RedisClient 是个接口,而且是第三方库。所以很难扩展
    tangtj
        8
    tangtj  
       2019-05-26 12:57:16 +08:00
    可以使用 ApplicationContext 运行时根据配置文件动态获取 bean
    xiangyuecn
        9
    xiangyuecn  
       2019-05-26 12:57:29 +08:00
    @beryl #7 呆板😂

    根据单点或集群分别实现 MyRedisClient 底层调用逻辑
    MyXXOO_RedisClient implements MyRedisClient 里面调用了 RedisClient 底层逻辑
    MyXXOO_RedisClusterClient implements MyRedisClient 里面调用到了 RedisClusterClient 底层逻辑

    通过配置给 client 动态赋值
    private MyRedisClient redisClient=new (MyXXOO_RedisClient or MyXXOO_RedisClusterClient)
    undeflife
        10
    undeflife  
       2019-05-26 13:01:18 +08:00
    @beryl 第三方库你同样可以自己再封装一层。
    这个场景最合适使用 @Conditional
    上面的代码我随手写的 有些错漏,
    随手收了个例子 你可以看看
    https://reflectoring.io/spring-boot-conditionals/
    notreami
        11
    notreami  
       2019-05-26 13:09:28 +08:00
    适配模式?门面模式?
    beryl
        12
    beryl  
    OP
       2019-05-26 13:11:24 +08:00
    @undeflife 我看下,不过现在是用的 spring mvc 4.x 支持 @Conditional, 应该有些细微差别。我研究下
    beryl
        13
    beryl  
    OP
       2019-05-26 13:12:57 +08:00
    @undeflife 不明白的一点是,我写了自己的条件,但是在声明的地方还是没看懂怎么写。

    原先通过
    ```
    @Resource("testService")
    private RedisClient redisClient;

    ```

    那么现在这个地方如果声明呢
    mooncakejs
        14
    mooncakejs  
       2019-05-26 13:14:34 +08:00 via iPhone
    xml 就很方便了
    codeyung
        15
    codeyung  
       2019-05-26 13:14:56 +08:00
    多数据源配置 getredis 根据条件传入不同的 redis 实例 你去搜索一下 应该是就改一个 redisService 就好了
    beryl
        16
    beryl  
    OP
       2019-05-26 13:15:49 +08:00
    @mooncakejs 怎么个方便呢?
    undeflife
        17
    undeflife  
       2019-05-26 13:45:09 +08:00
    @beryl Conditional 会根据条件决定是否创建 bean,所以你只会有一个满足条件的 bean 存在,你使用的地方修改为 by type 而不是默认的 by name 即可
    lihongjie0209
        18
    lihongjie0209  
       2019-05-26 13:53:14 +08:00
    你直接注入一个 redisServiceFactory 不就好了?
    beryl
        19
    beryl  
    OP
       2019-05-26 14:19:29 +08:00
    @lihongjie0209 现在 cluster 是个 factory, 另一个也是 factory,
    尝试封装,一个更高层的 factory, 但是没有成功
    Takamine
        20
    Takamine  
       2019-05-26 15:45:00 +08:00 via Android
    看到是根据不同的配置,那我就觉得楼上 @Conditional 合适。_(:з」∠)_
    beryl
        21
    beryl  
    OP
       2019-05-26 17:04:16 +08:00
    @undeflife 有没有一种,不满足『最好有实现相同的接口或父』,的实现方式呢,thx
    undeflife
        22
    undeflife  
       2019-05-26 18:00:11 +08:00
    @beryl 使用工厂类咯
    beryl
        23
    beryl  
    OP
       2019-05-26 20:04:54 +08:00
    @undeflife 对设计模式不是很清楚,虽然不是很懂, 有种朦胧感, 不过还是感谢。
    我自己再研究下吧。
    xuanbg
        24
    xuanbg  
       2019-05-27 07:46:36 +08:00
    没明白什么需求,只看到一个集群一个单机的区别。但 Redis 功能上来讲集群和单机并没有什么不同啊?为啥要写两个方法?
    yxjn
        25
    yxjn  
       2019-05-27 10:50:55 +08:00
    spring boot 很简单,只要在配置文件里修改即可。注册的 redistemplate 即是相应的 redis 配置下的。
    palmers
        26
    palmers  
       2019-05-27 11:48:49 +08:00
    我理解 lz 的需求是需要动态切换 redis 的数据源, 在向上抽象一个委托类或适配器来执行业务 然后利用观察者模式 或者工厂 bean 来切换 redis 数据源 我觉得这个方法可以办到
    beryl
        27
    beryl  
    OP
       2019-05-27 13:57:43 +08:00
    @palmers 大体思路可以明白,可能是现在对这些不太透彻理解,所以细节实现还是没能实现。
    beryl
        28
    beryl  
    OP
       2019-05-27 13:58:03 +08:00
    @yxjn 然而不是 spring boot ...
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   4583 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 30ms · UTC 05:35 · PVG 13:35 · LAX 22:35 · JFK 01:35
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.