2

我有一项与经纪账户的 API 交互的服务。它工作正常,但现在我需要与同一家经纪公司的两个不同账户进行交互。

处理此问题的最佳方法似乎是可以配置服务以指定目标帐户,然后实例化两个不同的实例,一个用于每个帐户。

我不确定 Grails 是否支持这一点或如何去做。

两个问题:

  1. 有一个更好的方法吗?
  2. 如果没有,我如何实例化和配置两个不同的服务实例?

附加信息:

这两个答案都差强人意。让我试着澄清一下:

我不想深入细节,但这可能有助于解释我所追求的。我使用的是盈透证券交易 API,他们不会像其他经纪公司那样让您直接与他们的服务器对话。您必须通过套接字与他们的 IB 网关通信,这是他们提供的一个软件,基本上代理了他们的服务器。因此,您的应用程序与 IB 网关通信,而 IB 网关代表您的应用程序与盈透证券的服务器通信。

问题是 IB 网关必须作为其配置的一部分登录到一个帐户。因此,为了交易两个不同的账户,您别无选择,只能配置两个不同的 IB 网关,因为每个网关只能访问为其配置的账户。

因此,我用于进行交易的 Grails 代码必须选择正确的 IB 网关进行通信。这意味着它需要知道每个账户对应的 IB 网关的 IP 地址和端口。除了 IP 地址和端口的这个设置之外,与 IB 网关通信的两个 Grails 服务之间没有区别。

我想要的是重用相同的服务类,每个服务类都被实例化为一个单例,只是具有不同的 IP 地址和通信端口。

所以制作两个不同的服务是不可取的,因为代码在其他方面是相同的。(如果我添加第三个或第四个 IB 网关,这将变成相当臭的代码。)

而且这个设置应该存在于应用程序的生命周期中,所以我认为范围的改变也不是真正的答案。

我真的想要同一服务的两个实例,只是具有不同的配置。

我希望这有助于解释这种情况。你有什么建议?谢谢!

4

2 回答 2

2

如果相同的业务逻辑适用于两个帐户,但考虑到您不能让单个服务类与两个帐户的 API 对话,那么是的,您可以使用 2 个服务类(只不过是 2 个不同的 spring bean)默认单例范围。

class Account1Service{
}

class Account2Service{
}

如果我有可以共享的通用逻辑,我也会尝试在这种情况下是否可以在这里使用继承。但请记住,如果您从抽象类继承服务类,则必须将抽象类放置在src/groovy外部/grails-app/,以对抗依赖注入。在这种情况下,您最终可能会得到(未经测试,但您可以坚持 DRY 概念)

// src/groovy
abstract class BrokerageService {
    def populateAccountDetails(Long accountId)
    def checkAccountStatus(Long accountId)
}

//grails-app/services
class Account1Service extends BrokerageService {
    //Implement methods + add logic particular to Account1
    //By default transacitonal
}

class Account2Service extends BrokerageService {
    //Implement methods + add logic particular to Account2
    //By default transacitonal
}

还要注意范围是singleton,您会格外小心(最好避免)维护 Service 类中的全局范围属性。尽量做到无状态。除非情况或业务逻辑要求使用 , 或 等服务级别范围sessionflow否则request我将始终坚持使用默认singleton范围。

要回答您的第二个问题,您不需要实例化任何 grails 服务类。当使用适当的命名法时,容器会注入适当的服务类(使用 Spring IoC)。在上面的示例中,如果您在要使用服务的类中遵循此命名约定,则会自动注入服务类:

//camelCase lower initial
def account1Service 
def account2Service

更新
这是对 OP 提供的附加信息的回应。

参考上述场景,默认范围内只能有一个service类可以singleton完美处理事情。最好的部分是,由于您要离开网络并且并不真正担心自己的数据库事务,因此可以将服务类设置为非事务性的。但这又取决于情况需要。这是服务类的样子。

//grails-app/service
class BrokerageService{

    //Service method to be called from controller or any endpoint
    def callServiceMethod(Long accountId){
        .......
        doSomethingCommonToAllAccounts()
        .........

        def _ibConfig = [:] << lookupIBGatewayConfigForAccount(accountId)
        ........

        //Configure an IB Gateway according to the credentials
        //Call IB Gateway for Account using data got from _ibConfig map
        //Call goes here 
    }

    def doSomethingCommonToAllAccounts(){
        ........
        ........
    }

    def lookupIBGatewayConfigForAccount(accountId){
        def configMap = [:]
        //Here lookup the required IP, account credentials for the provided account
        //If required lookup from database, if you think the list of accounts would grow
        //For example, if account is JPMorgan, get credentials related to JPMorgan

        //put everything in map
        configMap << [ip: "xxx.xx.xx.xxx", port: 80, userName: "Dummy"] //etc

        return configMap
    }
}

服务类的范围是单例的,这意味着堆中只有一个类的实例,这也意味着任何类级别的属性(方法除外)都是有状态的。在这种情况下,您只处理无状态且足以满足目的的方法。您将获得所需的东西,而无需花费堆或BrokerageService每次交易发生时都创建新实例。

每笔交易(关联账户)最终都会调用服务,从 db(或配置属性、平面文件或属性文件)中查找凭证,然后配置 IB 网关并调用/与网关通话。

于 2013-06-20T01:40:20.113 回答
1

默认情况下, Grails 服务应该是单例的,没有任何与其正在执行的操作相关联的状态,并且通常只有一个实例。也就是说,通常情况下,您不会在其中包含实例字段。

但是,如果您覆盖默认范围,则可以拥有它们。例如,您可以使您的服务成为会话范围,添加这个静态变量:

static scope = "session"

然后,您将为每个用户会话创建一个实例。

对于您的特定情况,您可能需要查看原型范围,它会在您每次需要注入服务时为您提供一个新的服务实例。如果您希望它们作用于相同的数据,您只需要确保在注入后始终使用该实例。

查看有关 Scoped Services 的文档

于 2013-06-20T01:18:07.397 回答