Java + Command Pattern + Spring + remoting:如何向Command对象注入依赖项?

时间:2010-01-26 15:19:55

标签: java spring client-server command-pattern

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

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

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

  1. 客户端创建一个命令并对其进行参数化
  2. 客户端将命令发送到服务器
  3. 可以提供服务器接收命令,日志,审计和断言命令
  4. 服务器执行命令
  5. 服务器返回命令结果到客户端
  6. 问题发生在第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");这样的电话 并改为注入我的服务。

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

6 个答案:

答案 0 :(得分:3)

您可以使用Spring @Configurable批注允许AspectJ将Spring bean注入不是由Spring创建的对象中。看看here

答案 1 :(得分:2)

  

这不是理解的问题   DI如何运作

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

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

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

  2. Spring,作为容器,假定您要使用作用域(session,prototype = thread scope),应用程序范围创建对象一次......

  3. 因此命令不是由spring创建的,它应该使用spring创建的服务。 但是该命令中没有注入该服务。

    谢谢

答案 2 :(得分:2)

在服务器端,在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对象的实例。

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

    如何使用AutowireCapableBeanFactory还有一些其他选项 - 此示例使用适当的类查找bean定义,并设置应用程序上下文中定义的属性。如果您可以将每个Command实现与应用程序上下文中的名称相关联,则可以使用configureBean(Object,String)来支持回调。

答案 3 :(得分:1)

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

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

Eclipse Rich Client - &gt; (Spring Remoting - HTTP) - &gt; Servlet - &gt; (本地接口) - &gt; EJB - &gt;服务实施

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

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

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

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

答案 4 :(得分:0)

如果通过Spring ApplicationContext将SimpleCommand注入到类中(实际上应该是这样),那么你只需要将它的依赖关系表达为构造函数参数或setter并注入它们。

如果不了解谁在使用SimpleCommand,它来自何处,等等,很难再提供更多细节。

答案 5 :(得分: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");

依赖注入不依赖于框架。