【问题标题】:How to use the strategy pattern with managed objects如何将策略模式与托管对象一起使用
【发布时间】:2019-08-20 16:07:01
【问题描述】:

我处理来自队列的消息。我使用来自传入消息的数据来确定使用哪个类来处理消息;例如来源和类型。我会使用 origin 和 type 的组合来查找 FQCN 并使用反射来实例化一个对象来处理消息。目前这些处理对象都是简单的 POJO,它们实现了一个通用的接口。因此,我使用的是策略模式。

我遇到的问题是我的所有外部资源(主要是通过 JPA 访问的数据库)都被注入 (@Inject) 并且当我如上所述创建处理对象时,所有这些注入的对象都是空的。我知道填充这些注入资源的唯一方法是通过添加@stateless 使接口的每个实现成为托管bean。仅此一项并不能解决问题,因为只有在实现接口的类本身是注入的(即容器管理的)而不是由我创建的情况下,才会填充注入的成员。

这是一个虚构的例子(敏感细节已更改)

public interface MessageProcessor
{
   public void processMessage(String xml);
}

@Stateless
public VisaCreateClient implements MessageProcessor
{
   @Inject private DAL db;
   …
}
public MasterCardCreateClient implements MessageProcessor…

在数据库中有一个条目“visa.createclient”=“fqcn.VisaCreateClient”,所以如果消息来源是“Visa”并且类型是“Create Client”,我可以查找相应的处理类。如果我使用反射来创建 VisaCreateClient,则 db 变量始终为空。即使我添加@Stateless 并使用反射,db 变量仍然为空。只有当我注入 VisaCreateClient 时,db 变量才会被填充。像这样:

@Stateless
public QueueReader
{
   @Inject VisaCreateClient visaCreateClient;
   @Inject MasterCardCreateClient masterCardCreateClient;
   @Inject … many more times

   private Map<String, MessageProcessor> processors...

   private void init()
   {
      processors.put("visa.createclient", visaCreateClient);
      processors.put("mastercard.createclient", masterCardCreateClient);
      … many more times
   }
}

现在我有几十个消息处理器,如果我必须注入每个实现然后在映射中注册它,我最终会注入几十个。此外,如果我添加更多处理器,我必须修改 QueueReader 类以添加新注入并重新启动服务器;使用我的旧代码,我只需要在数据库中添加一个条目并在类路径上部署新处理器 - 甚至不必重新启动服务器!

我想了两种方法来解决这个问题:

  1. 将 init(DAL db, OtherResource or, ...) 方法添加到在使用反射创建消息处理器并传递所需资源后立即调用的接口。资源本身被注入到 QueueReader。
  2. 向 processMessage(String xml, Context context) 添加一个参数,其中 Context 只是注入到 QueueReader 中的资源映射。

但是这种方法是否意味着我将为每个消息处理器使用相同的 DAL 对象实例?我相信它会,只要不涉及任何状态,我相信它是可以的 - 任何和所有事务都将在 DAL 类之外启动。

所以我的问题是我的方法会奏效吗?这样做有什么风险?有没有更好的方法来使用策略模式来动态选择需要访问容器管理资源的实现?

感谢您的宝贵时间。

【问题讨论】:

  • 这不是 jms 为你做的主题吗?
  • 可能——我刚才只是简单地研究了一下,你能推荐一个好的资源来了解更多吗?

标签: cdi strategy-pattern


【解决方案1】:

在类似的问题陈述中,我使用了处理器接口的扩展来决定它可以处理哪种类型的数据对象。然后您可以通过实例注入处理程序的所有变体并简单地使用循环:

public interface MessageProcessor
{
   public boolean canHandle(String xml);
   public void processMessage(String xml);
}

在你的 queueReader 中:

@Inject
private Instance<MessageProcessor> allProcessors;

public void handleMessage(String xml) {
    MessageProcessor processor = StreamSupport.stream(allProcessors.spliterator(), false)
        .filter(proc -> proc.canHandle(xml))
        .findFirst()
        .orElseThrow(...);
    processor.processMessage(xml);
}

这不适用于正在运行的服务器,但要添加新处理器,只需实现和部署即可。

【讨论】:

猜你喜欢
  • 2022-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-02-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多