1

在我当前的项目中,我正在处理实现巨大接口的 EJB。实现是通过业务委托完成的,业务委托实现相同的接口并包含真实的业务代码。

正如一些文章所建议的那样

这个“命令模式”的使用顺序是

  1. 客户端创建一个命令并参数化它
  2. 客户端向服务器发送命令
  3. 可以提供服务器接收命令、日志、审计和断言命令
  4. 服务器执行命令
  5. 服务器返回命令结果给客户端

问题发生在第 4 步:

现在我正在使用 spring 上下文从命令内部的上下文中获取 bean,但是我想将依赖项注入到命令中。

这是用于说明目的的幼稚用法。我在有问题的地方添加了评论:

public class SaladCommand implements Command<Salad> {    
    String request;

    public SaladBarCommand(String request) {this.request = request;}

    public Salad execute() {    
        //this server side service is hidden from client, and I want to inject it instead of retrieving it
        SaladBarService saladBarService = SpringServerContext.getBean("saladBarService");       
        Salad salad = saladBarService.prepareSalad(request);       
        return salad;
    }
}

public class SandwichCommand implements Command<Sandwich> {    
    String request;

    public SandwichCommand(String request) {this.request = request;}

    public Sandwich execute() {  
        //this server side service is hidden from client, and I want to inject it instead of retrieving it      
        SandwichService sandwichService = SpringServerContext.getBean("sandwichService");       
        Sandwich sandwich = sandwichService.prepareSandwich(request);       
        return sandwich;
    }
}

public class HungryClient {
    public static void main(String[] args) {
        RestaurantService restaurantService = SpringClientContext.getBean("restaurantService");
        Salad salad = restaurantService.execute(new SaladBarCommand(
            "chicken, tomato, cheese"
        ));
        eat(salad);

        Sandwich sandwich = restaurantService.execute(new SandwichCommand(
            "bacon, lettuce, tomato"
        ));
        eat(sandwich);
    }
}

public class RestaurantService {
    public <T> execute(Command<T> command) {
        return command.execute();
    }
}

我想摆脱类似的调用SandwichService sandwichService = SpringServerContext.getBean("sandwichService"); 并注入我的服务。

如何做到这一点最简单的方法?

4

6 回答 6

3

理解 DI 的工作原理不是问题

我同意纪尧姆的观点,因为我遇到了同样的情况。

主要问题是实例化和对象生命周期。

  1. 该命令可以多次创建,例如相同的方法。并以编程方式。

  2. Spring,作为容器,假设您想要创建一次具有范围(会话,原型 = 线程范围)的对象,应用程序范围......

所以该命令不是spring创建的,应该使用spring创建的服务。但是该服务没有注入命令中。

谢谢

于 2011-06-10T09:38:35.890 回答
3

您可以使用 Spring @Configurable 注解来允许 AspectJ 将 Spring bean 注入到不是由 Spring 创建的对象中。看看这里

于 2011-11-01T02:52:13.620 回答
3

在服务器端,在 RestaurantService 中,执行以下操作:

  1. 实现 ApplicationContextAware 接口——这将导致 Spring 将应用程序上下文的引用注入到 RestaurantService。

  2. 在 RestaurantService.execute(Command) 方法中,执行以下操作:

    AutowireCapableBeanFactory beanFactory = applicationContext.getAutowireCapableBeanFactory();
    beanFactory.autowireBeanProperties(command, AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, true);
    command.execute();
    
  3. 最后,在您的应用程序上下文中,声明每个具有注入依赖项的 Command 对象的实例。

这应该具有允许您在客户端上创建对象、序列化它们、将它们发送到服务器以及在使用它们之前注入依赖项的效果。当您收到物品而不是在您使用它们之前进行注射可能更清洁。

关于如何使用 AutowireCapableBeanFactory 还有一些其他选项——这个例子找到了一个具有适当类的 bean 定义,并设置了在应用程序上下文中定义的属性。如果您可以将每个 Command 实现与应用程序上下文中的名称相关联,则可以使用 configureBean(Object, String) 代替,它将支持回调。

于 2013-09-11T15:48:50.870 回答
1

我在过去构建了一些惊人的相似的东西,除了我们没有像你现在那样使用命令模式。在您的情况下,您的命令似乎除了实际查找和运行服务方法之外什么都不做,那么为什么不简单地将该服务方法呈现为 API 而不是完全使用命令模式。然后,您可以通过 Spring Remoting 将服务调用连接到 EJB,所有 Spring 细节都可以保留在协议特定层(Servlet、EJB、MDB ...)中,并且您的代码完全不知道周围发生了什么.

我们的基础设施看起来像这样。(对于那些将抱怨 EJB 存在的人,这并不是整个基础架构,出于安全和性能原因,我们使用 EJB 到 EJB 调用来进行服务到服务的交互)。

Eclipse 富客户端 -> (Spring Remoting - HTTP) -> Servlet -> (本地接口) -> EJB -> 服务实现

Servlet - 使用 Spring 上下文查找本地 EJB 接口,并使用 RemoteInvocation 对象(由 Spring Remoting 的 HttpProxyFactoryBean 生成和发送)和服务接口的名称调用通用 EJB 接口的公共调用方法。

EJB - 根据其接口名称(也是 bean 名称)查找服务,并使用 RemoteInvocationExecutor 通过 RemoteInvocation 对象调用服务实现上的方法。

现在 EJB 能够绑定到多个服务(尽管我们使用一对一的部署模型)。您可以将 Spring Remoting 用于来自不同应用程序的基于 Http、EJB 或 JMS 的服务调用。没有服务器部署的测试是微不足道的,因为您只需将测试直接连接到实现。

注意:如果有机会,我会尝试添加一些代码片段。

于 2010-01-26T16:00:45.363 回答
0

如果 Spring ApplicationContext 将 SimpleCommand 注入到使用它的类中(实际上应该如此),那么您只需将它的依赖关系表达为构造函数参数或设置器并注入它们。

如果不了解谁在使用 SimpleCommand、它来自哪里等,就很难给出更多细节。

于 2010-01-26T15:43:07.510 回答
0
public class SampleCommand implements Command {    
    private final String parameter;
    private final ServiceBean service;

    //the client build the command using a parameter
    public SampleCommand(ServiceBean service, String parameter) {
         this.parameter = parameter;
         this.service = service;
    }

    //this code will be executed by the server
    public Object execute() {
        //do something using the parameter and return the result
        return service.doSomethingWith(parameter);            
    }
}

您可以使用或不使用 Spring 注入服务:

<bean id="sampleCommand" class="package.SampleCommand">
     <constructor-arg ref="serviceBean" />
     <constructor-arg value="Param" />
</bean>

一些通话应用程序:

ServiceBean service = ServiceProxy.getService(SampleCommand.class);
Command command = new SampleCommand(service, "Param");

依赖注入不依赖于框架。

于 2010-01-26T16:38:39.100 回答