Java + Command Pattern + Spring + remoting: Как внедрить зависимости в объект Command? - PullRequest
0 голосов
/ 26 января 2010

В моем текущем проекте я имею дело с EJB, реализующими огромные интерфейсы. Реализация осуществляется через бизнес-делегата, который реализует тот же интерфейс и содержит настоящий бизнес-код.

Как предлагают некоторые статьи, такие как

Последовательность использования этого 'шаблона команды'

  1. клиент создает команду и параметризует ее
  2. клиент отправляет команду на сервер
  3. может быть получена команда приема, регистрации, аудита и утверждения на сервере
  4. сервер выполняет команду
  5. результат команды возврата сервера клиенту

Проблема возникает в шаге 4.:

Сейчас я использую контекст весны, чтобы получить бин из контекста внутри команды, но я хочу ввести зависимости в команду.

Вот наивное использование для иллюстрации. Я добавил комментарии, где у меня есть проблемы:

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 ]

3 голосов
/ 01 ноября 2011

Вы можете использовать аннотацию Spring @Configurable, чтобы позволить AspectJ внедрять компоненты Spring в объекты, которые не созданы Spring. Взгляните здесь

2 голосов
/ 11 сентября 2013

На стороне сервера в 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 - в этом примере выполняется поиск определения компонента с соответствующим классом и установка свойств, определенных в контексте приложения. Если вы можете связать каждую реализацию Command с именем в контексте приложения, вместо этого вы можете использовать configureBean (Object, String), который будет поддерживать обратные вызовы.

2 голосов
/ 10 июня 2011

Это не проблема понимания как работает DI

Я согласен с Гийомом, потому что я встречаюсь с этим же случаем.

Основная проблема - инстанцирование и жизненный цикл объекта.

  1. Команда может быть создана много раз, например, одним и тем же методом. И программно.

  2. Spring как контейнер предполагает, что вы хотите создать объект один раз с областью действия (сеанс, прототип = область потока), областью применения ...

Таким образом, команда не создана весной, и она должна использовать сервис, созданный весной. Но служба не вводится в команду.

Спасибо

1 голос
/ 26 января 2010

В прошлом я создавал нечто удивительно похожее, за исключением того, что мы не использовали шаблон команды, как вы делаете в настоящее время. В вашем случае ваши команды, похоже, ничего не делают, кроме как на самом деле искать и запускать метод службы, так почему бы просто не представить этот метод службы как API, а не использовать шаблон команды вообще. Затем вы можете подключить сервисные вызовы к EJB с помощью Spring Remoting, и все особенности Spring могут оставаться на уровне протокола (Servlet, EJB, MDB ...), и ваш код остается удивительно неосведомленным о том, что происходит вокруг него. .

Наша инфраструктура выглядит следующим образом. (Для тех, кто собирается жаловаться на существование EJB, это не вся инфраструктура, и по соображениям безопасности и производительности мы используем вызовы EJB к EJB для взаимодействия между сервисами).

Eclipse Rich Client -> (Spring Remoting - HTTP) -> Сервлет -> (Локальный интерфейс) -> EJB -> Реализация службы

Сервлет - Использование контекста Spring для поиска локального интерфейса EJB и вызова общего метода вызова универсального интерфейса EJB с объектом RemoteInvocation (созданным и отправленным HttpProxyFactoryBean из Spring Remoting) и именем интерфейса службы.

EJB - Поиск службы на основе ее имени интерфейса (также является именем компонента) и использование RemoteInvocationExecutor для вызова метода реализации службы с объектом RemoteInvocation.

Теперь EJB может быть привязан к нескольким сервисам (хотя мы используем модель развертывания один к одному). Вы можете использовать Spring Remoting для вызовов службы на основе Http, EJB или JMS из разных приложений. Тестирование без развертывания сервера тривиально, поскольку вы просто подключаете тесты непосредственно к реализациям.

Примечание. Я постараюсь добавить фрагменты кода, если получу такую ​​возможность.

0 голосов
/ 26 января 2010
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");

Внедрение зависимостей не зависит от структуры.

0 голосов
/ 26 января 2010

Если SimpleCommand внедряется в классы, использующие его с помощью Spring ApplicationContext (и действительно, так и должно быть), то вам просто нужно выразить его зависимости как аргументы-конструкторы или установщики и также внедрить их.

Трудно дать какие-либо подробности, не понимая, кто использует SimpleCommand, откуда он и т. Д.

...