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

springboot 中 controller 中通过一个 key 值动态调用 service

  •  
  •   jiobanma ·
    banmajio · 2020-09-03 11:03:22 +08:00 · 4913 次点击
    这是一个创建于 1567 天前的主题,其中的信息可能已经有所发展或是发生改变。

    想在服务启动的时候,将所有的 service 存放到一个缓存 Map 中,然后给定一个 key 值绑定。 在 controller 中通过 key 值找到对应的 service 对象该如何实现。 因为不同 service 的类型不一样,所以 Map 在定义的时候只能定义成 Map<String,Object>value 是个 Object, 这样的话通过 key 值取出来的也就是 Object 的类型,无法调用到 service 下的方法。 有没有好的实现思路呢?

    50 条回复    2020-09-07 17:34:35 +08:00
    tsening
        1
    tsening  
       2020-09-03 11:16:12 +08:00
    我累了,小白吧。。。
    mosliu
        2
    mosliu  
       2020-09-03 11:17:00 +08:00
    反射呗
    chendy
        3
    chendy  
       2020-09-03 11:17:41 +08:00
    为啥要这么搞,别的不说,service 的参数和返回也不一样啊
    jiobanma
        4
    jiobanma  
    OP
       2020-09-03 11:23:43 +08:00
    @chendy #3 因为服务要做成动态的,service 里面只有一个通用接口,参数都是 Map 所以不存在返回值类型和参数不同的问题
    wysnylc
        5
    wysnylc  
       2020-09-03 11:31:53 +08:00
    @jiobanma #4 我只能说,作死
    说了多少回参数和返回值不要用 map,不撞南墙不回头
    pushback
        6
    pushback  
       2020-09-03 11:32:00 +08:00
    method.invoke
    pushback
        7
    pushback  
       2020-09-03 11:33:09 +08:00
    存储 class 就行,对应 value 到 BeanFactory 下去取
    idoggy
        8
    idoggy  
       2020-09-03 11:35:01 +08:00 via Android   ❤️ 1
    入参都一样吗?要调用的 service 全部继承同一个接口就行了,用 applicationcontext.getbean 去调。
    ily433664
        9
    ily433664  
       2020-09-03 11:40:21 +08:00
    通过 applicationContext 获取,可以根据 bean 类型或者名称
    YzSama
        10
    YzSama  
       2020-09-03 11:42:44 +08:00
    @idoggy #8 这个方式可取。 不过,入参和出参不建议使用 map 。
    lipcao
        11
    lipcao  
       2020-09-03 11:56:22 +08:00
    哈哈,你这个标准的策略+工厂模式 解决 if else 啊,前一段时间公众号嗷嗷推这个。。。
    zhuweiyou
        12
    zhuweiyou  
       2020-09-03 11:57:32 +08:00   ❤️ 1
    建议楼主学 PHP 或 JS,他们就是这么玩的。
    DelayNoMay
        13
    DelayNoMay  
       2020-09-03 12:00:08 +08:00
    用 golang 的 interface
    yuan7712
        14
    yuan7712  
       2020-09-03 12:03:11 +08:00
    460881773
        15
    460881773  
       2020-09-03 12:04:35 +08:00
    花里胡哨
    aguesuka
        16
    aguesuka  
       2020-09-03 12:06:47 +08:00 via Android
    service 都带个 controller,原来的 controller 里通过 key 转发到对应的 service 的 controller 。param bind 是 spring 的工作,不要实现一个低配版的
    aguesuka
        17
    aguesuka  
       2020-09-03 12:10:16 +08:00 via Android   ❤️ 1
    以前我还是个傻逼的时候,map 里面存的是个 lambda 函数,比如 map.put(key,xxxxService::doxxxxxxx)
    huifer
        18
    huifer  
       2020-09-03 12:17:25 +08:00
    添加一个 接口比如
    ```java
    public interface KeyInterface{
    String key();
    }

    ```
    - 所有 service 都实现这个接口,重写 key()

    - 在启动阶段(ApplicationRunner 或者 CommandLineRunner)通过 context.getBeanOfTypes(KeyInterface.class) 获取到一个 map
    key: beanName value: KeyInterface 的实现
    - 调用 value 的 key()方法. 将结果映射成 key:key() ,value: service 即可
    dutianze
        19
    dutianze  
       2020-09-03 13:11:45 +08:00
    如果所有 sevice 都是同样的接口的话,

    1. 所有 service 都继承一个接口 CommonService

    2.
    @Autowired
    private Map<String, CommonService> commonServiceMap;

    k 默认是 service 的类名小写,v 默认是对应的 service
    butterfly1211
        20
    butterfly1211  
       2020-09-03 13:13:28 +08:00
    服务注册与服务发现
    24bit
        21
    24bit  
       2020-09-03 13:16:57 +08:00
    不如直接用注解标注要缓存的方法,直接在 Map 里面存 Method,取的时候直接调用
    Cyron
        22
    Cyron  
       2020-09-03 13:19:33 +08:00
    Service service = applicationContext.getBean("serviceImplA");
    Service service = applicationContext.getBean("serviceImplB");
    jintianfengda
        23
    jintianfengda  
       2020-09-03 14:28:27 +08:00
    用 map 这么玩开发一时爽,维护火葬场
    ixx
        24
    ixx  
       2020-09-03 14:55:18 +08:00
    liuguangcuican
        25
    liuguangcuican  
       2020-09-03 15:12:58 +08:00 via Android
    定义一个 interface,在其每个实现类上使用 @Component("实现类的别名")。在 controller 里使用 @Autowrid 定义一个 Map<String,你定义的 interface>,在入口方法里使用 map.get("实现类的别名")就能获取到你对应的实现类。
    d460686680
        26
    d460686680  
       2020-09-03 15:24:21 +08:00
    实现同一个接口 + getBean()
    levizheng
        27
    levizheng  
       2020-09-03 15:36:41 +08:00
    简单工厂就可以解决了。。 放一 map 里
    xuanbg
        28
    xuanbg  
       2020-09-03 16:16:29 +08:00
    我司的 php 写的老系统就是这样,接口参数里面有个 action,1234567……分别对应一个方法。。。。各方法传入一个 object,其实就是一个 json,各个方法自己负责解析这个参数。楼主莫非要的是这个?

    可是为啥不把接口拆开呢?
    KevinBlandy
        29
    KevinBlandy  
       2020-09-03 16:26:15 +08:00
    ApplicationContext.getBean();
    ocean1477
        30
    ocean1477  
       2020-09-03 16:30:24 +08:00
    策略模式了解下
    caotian
        31
    caotian  
       2020-09-03 16:38:23 +08:00
    不要手工维护 map, 不然每次加一个 serivce 还要修改这个 map
    可以这样试试: 每个 service 定义自己的 key, 实现同一个接口 IService, 使用的时候通过 Autowired 注入拿到 List<IService>对象, 通过 key 来获得具体的 service 实例, 再调用方法
    jiobanma
        32
    jiobanma  
    OP
       2020-09-03 16:40:36 +08:00
    @wysnylc #5 业务需要
    pierswu
        33
    pierswu  
       2020-09-03 16:52:43 +08:00
    这个用 mvc 的 forward 不就行了吗?

    每一个 service 都有各自的 controller,就按照正常的写 ,然后总的 controller 根据不同的 key,forward 到不同的 controller
    至于 key,最好能放在 url 中或者 header 中,好取一些
    pierswu
        34
    pierswu  
       2020-09-03 16:57:54 +08:00
    用 forward 的话,每个 service 的参数类型,参数个数,返回值,都可以不一样的
    pengfei
        35
    pengfei  
       2020-09-03 17:31:53 +08:00
    策略模式 ?
    venpong
        36
    venpong  
       2020-09-03 18:05:50 +08:00
    建议学学设计模式,比如楼上说的策略模式就可以很优雅的解决你这个问题。
    oneisall8955
        37
    oneisall8955  
       2020-09-03 18:24:36 +08:00 via Android   ❤️ 1
    我最喜欢搞这些花里胡哨得了,经常写工厂模式+模板
    securityCoding
        38
    securityCoding  
       2020-09-03 18:40:46 +08:00
    applicationContext.getBean()

    策略模式+泛型可解

    不过在业务层做这种事情这种看起来很无脑....中间件还情有可原
    BigBunny
        39
    BigBunny  
       2020-09-03 20:03:39 +08:00
    做那种对外开放平台标准化接口有用过。当时的想法是 controller 里所有接口的请求参数返回参数都一样。加签验签逻辑也都一致。索性共用一个接口,调用 service 里的方法通过自定义注解去区分。半路转的 java,也不知道这么做了对不对好不好。
    supermoonie
        40
    supermoonie  
       2020-09-03 20:12:26 +08:00 via iPhone
    注解?
    chachae
        41
    chachae  
       2020-09-03 20:45:55 +08:00
    策略模式
    gaius
        42
    gaius  
       2020-09-04 01:23:30 +08:00 via Android
    以前掉各种银行接口就是用反射,配置全限定名
    1ffree
        43
    1ffree  
       2020-09-04 09:56:48 +08:00
    19l 正解 +1
    HolmLoh
        44
    HolmLoh  
       2020-09-04 10:26:26 +08:00
    工厂模式加模板模式,小的单人开发的项目这么搞搞还行,偷偷懒还挺快乐,人多起来不好维护
    yisheyuanzhang
        45
    yisheyuanzhang  
       2020-09-04 10:51:46 +08:00
    想到两种方法
    1 、8L 的方法,Service 实现同一接口,key 作为 bean 的名称,用 applicationcontext.getbean 去调。缺点是每个 service 只能实现同一个方法。
    2 、定义 service 的类注解和方法注解, 启动时通过 BeanPostProcessor,根据类注解的 key 存入 Map<key,bean>中。调用时根据类注解 key,找到 bean,根据方法注解 key 找到方法,通过反射执行

    我们生产中用过第二种
    CoderGeek
        46
    CoderGeek  
       2020-09-04 13:54:15 +08:00
    简单点
    1. <String,String> key 是你的策略 value 是你的 beanName 直接 get 就好
    2. <String,Class> key 是你的策略 value 是你的所有加载的 service 取出来强制转型
    3.直接用 xml 更简单创建一个 map 对象 value 是多个 bean
    举个常见栗子
    private Map<Object, Object> targetDataSources;
    <bean id="xxx" class="xxxxx">
    <property name="targetDataSources">
    <map>
    <entry key="xxxxx" value-ref="xxxxx"></entry>
    </map>
    </property>
    </bean>
    6IbA2bj5ip3tK49j
        47
    6IbA2bj5ip3tK49j  
       2020-09-04 13:56:27 +08:00
    别听楼上 BB 。
    把那些 service 合成一个函数方法就 ok 了。
    一个函数就解决所有问题了,高内聚。
    传来 map,返回 map,改动也只需要改这一个函数,低耦合。
    xiaoxinshiwo
        48
    xiaoxinshiwo  
       2020-09-04 15:38:59 +08:00
    instance of
    yazinnnn
        49
    yazinnnn  
       2020-09-05 22:14:12 +08:00
    ```kotlin
    @RestController
    class ControllerA(
    var serviceA: ServiceA,
    var serviceB: ServiceB
    ) {

    @RequestMapping("/")
    fun foo(type: String, param: String) = serviceMap[type]!!(param)


    private val funcA = { it: String -> serviceA.foo(it) }
    private val funcB = { it: String -> serviceB.foo(it) }


    private val serviceMap by lazy {
    mutableMapOf(
    "a" to funcA,
    "b" to funcB
    )
    }
    }
    ```

    kotlin 可以这么干,java 没有 by lazy,估计比较啰嗦
    Relims
        50
    Relims  
       2020-09-07 17:34:35 +08:00
    value 存 service 的实例化对象,通过对象来调用方法,如果你的 sevice 有共同的方法的化,还可以用到工厂模式
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3553 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 28ms · UTC 04:22 · PVG 12:22 · LAX 20:22 · JFK 23:22
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.