Spring autowire заглушенный сервис - дубликат bean-компонента

Ok. Нам нужно @Autowire другой веб-сервис на лету (предпочтительно, переключая настройку JNDI на веб-сервере), и я не знаю, как это сделать. Вот так я подходил к проблемам..

Два пакета: com.mycomp.service.stub com.mycomp.service.impl

Один пакет содержит MyServiceStub.java при реализации MyService. Другой пакет содержит MyServiceImpl.java, который реализует то же самое.

Мой контроллер, для которого требуется MyService, имеет bean-компонент, определенный как таковой

@Autowire
private MyService communicator;

Мой spring-context.xml имеет следующее:

<context:component-scan base-package="com.mycomp" />

В этот момент я получаю исключение DuplicateBean во время автоматического подключения. Теперь я могу статически определить, какой bean-компонент для автоматического связывания в spring-context.xml:

<bean id="communicator" class="com.mycomp.service.impl.MyServiceImpl" />

и все работает нормально... Но тогда как «щелкнуть» переключателем и перейти на метод Stub на нашем QA-сервере? Он не имеет подключения к этой службе, поэтому нам нужно работать с включенными заглушками. Свойство JNDI было бы лучше всего для этого... но я просто не могу понять, как переключать автопроводку bean spring во время выполнения.

Любая помощь??

Привет, Крис


person Chris Kannon    schedule 26.01.2012    source источник
comment
Как показано ниже от Tomasz! Профили Spring 3.1 созданы для вас!   -  person Alex Barnes    schedule 26.01.2012


Ответы (2)


@Profile решение

Вам обязательно нужно попробовать Spring 3.1 @Profile:

@Autowire
private MyService communicator;

//...

@Service
@Profile("prd")
class MyServiceImpl //...

@Service
@Profile("qa")
class MyServiceStub //...

Теперь в зависимости от того, какой профиль включен, будет инициализирован либо DefaultMyService, либо MyServiceStub.

Вы можете выбирать между профилями различными способами:

Spring AOP (явно для каждого метода)

В этом примере аспект обходит каждый отдельный метод MyService отдельно и возвращает заглушенное значение:

@Aspect
@Service
public class StubAspect {

    @Around("execution(public * com.blogspot.nurkiewicz.MyService.foo(..))")
    public Object aroundFoo(ProceedingJoinPoint pjp) throws Throwable {
        if (stubMode()) {
            return //stub foo() result
        }
        return pjp.proceed();
    }

    @Around("execution(public * com.blogspot.nurkiewicz.MyService.bar(..))")
    public Object aroundBar(ProceedingJoinPoint pjp) throws Throwable {
        if (stubMode()) {
            return //stub bar() result
        }
        return pjp.proceed();
    }

    private boolean stubMode() {
        //whatever condition you want here
        return true;
    }

}

Код довольно прост, к сожалению, возвращаемые значения скрыты внутри аспекта, и вам нужен отдельный @Around для каждого целевого метода. Наконец, нет места для MyServiceStub.

Spring AOP (автоматически вокруг всех методов)

@Aspect
@Service
public class StubAspect {

    private MyServiceStub stub = //obtain stub somehow

    @Around("execution(public * com.blogspot.nurkiewicz.MyService.*(..))")
    public Object aroundFoo(ProceedingJoinPoint pjp) throws Throwable {
        if (stubMode()) {
            MethodSignature signature = (MethodSignature)pjp.getSignature();
            Method method = signature.getMethod();
            return method.invoke(stub, pjp.getArgs());
        }
        return pjp.proceed();
    }

    private boolean stubMode() {
        //whatever condition you want here
        return true;
    }

}

Этот подход является более неявным, поскольку он автоматически оборачивает каждый целевой метод, включая новые методы, добавляемые в будущем. Идея проста: если stubMode() выключен, запустить стандартный метод (pjp.proceed()). Если он включен — запустите точно такой же метод с точно такими же параметрами, но на другом объекте (в данном случае — на заглушке).

Это решение намного лучше, так как требует меньше ручной работы (за счет использования необработанного отражения).

Обратите внимание, что если обе реализации MyService являются компонентами Spring (даже если одна из них аннотирована @Primary), вы можете столкнуться со странными проблемами. Но это должно быть хорошим началом.

Смотрите также:

person Tomasz Nurkiewicz    schedule 26.01.2012
comment
Дох! Мы слишком поздно в цикле разработки, чтобы перейти на 3.1 с 3.0. Можете ли вы предложить какие-либо способы борьбы с этим в 3.0? Хотя выглядит потрясающе. - person Chris Kannon; 26.01.2012
comment
@ChrisKannon: вы можете использовать Spring AOP для переноса каждого вызова на DefaultMyService и, если установлен флаг JNDI, перенаправить на MyServiceStub. Я постараюсь добавить несколько примеров позже, эта техника сработала для меня очень хорошо. Кстати, переход с Spring 3.0 на Spring 3.1 не такой уж большой шаг... - person Tomasz Nurkiewicz; 26.01.2012
comment
Пример АОП для этого был бы замечательным. Что касается Spring 3.1, я предложу его на следующей встрече, но я хотел бы сделать это из коробки с нашей существующей библиотекой, если это возможно. - person Chris Kannon; 26.01.2012
comment
@ChrisKannon: я обновил свой ответ, он должен стать хорошей отправной точкой. Теперь вы понимаете, почему @Profile такой милый :-). - person Tomasz Nurkiewicz; 26.01.2012
comment
Tomasz Я проверил вашу стратегию с помощью АОП, и она отлично работает, спасибо! Вот пример проекта: docs.google.com/. - person Frederic Conrotte; 18.03.2012

Возможно, вы можете заменить класс свойством и развернуть свое приложение с другими файлами свойств. Производственная версия будет содержать имя реального класса, в то время как версия QA будет содержать имя заглушки.

Возможно, это http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/beans.html#beans-factory-extension-factory-postprocessors.

person Community    schedule 26.01.2012