Spring @Автозапуск с общим factory -строчным beans

У меня есть набор классов со сложной схемой инициализации. В принципе, я начинаю с интерфейса, который мне нужно удержать, а затем создайте кучу вызовов, и я получаю объект, реализующий этот интерфейс.

Чтобы справиться с этим, я создал класс factory, который может, с учетом интерфейса, создать конечный объект. Я сделал этот factory в bean, а в XML я указал свою различную службу beans как созданный через этот объект factory с параметром интерфейса, который они будут реализовывать.

Это отлично работает, и я полностью получаю именно beans мне нужно. К сожалению, я хотел бы получить к ним доступ из моих классов контроллеров, которые обнаруживаются с помощью сканирования компонентов. Я использую @Autwired здесь, и кажется, что Spring понятия не имеет, какой тип объекта они есть, а так как @Autowired работает по типу, я SOL.

Использование @Resource (name= "beanName" ) здесь будет работать отлично, однако кажется странным использовать @Resource для некоторых beans и @Autowired для других.

Есть ли способ получить Spring, чтобы узнать, какой интерфейс создаст factory для каждого из этих beans без использования другого метода factory для каждого типа?

Я использую Spring 2.5.6, кстати, иначе я бы просто JavaConfig все это и забыл об этом.

Factory класс:

<T extends Client> T buildService(Class<T> clientClass) {
  //Do lots of stuff with client class and return an object of clientClass.
}

контекст приложения:

<bean id="serviceFactoryBean" class="com.captainAwesomePants.FancyFactory" />
<bean id="userService" factory-bean="serviceFactoryBean" factory-method="buildService">
   <constructor-arg value="com.captain.services.UserServiceInterface" />
</bean>
<bean id="scoreService" factory-bean="serviceFactoryBean" factory-method="buildService">
   <constructor-arg value="com.captain.services.ScoreServiceInterface" />
</bean>  

мой контроллер:

public class HomepageController {

   //This doesn't work
   @Autowired @Qualifier("userService") UserServiceInterface userService;

   //This does
   @Resource(name="scoreService") ScoreServiceInterface scoreService;
}

Ответ 1

Я предлагаю вам сделать шаблон factory еще на один шаг и реализовать свои заводы как классы Spring FactoryBean. Интерфейс FactoryBean имеет метод getObjectType(), который содержит вызовы, чтобы узнать, какой тип возвращает factory. Это дает вашему автоувеличиванию что-то, чтобы получить свои зубы, до тех пор, пока ваш factory возвращает разумное значение.

Ответ 2

У меня была аналогичная проблема, но для меня я хотел использовать одиночный factory для создания выстроенных реализаций моих авто-проводных зависимостей с использованием JMockit (рамки тестирования, которую я должен использовать).

Не найдя удовлетворительного решения на interwebs, я бросил вместе простое решение, которое работает очень хорошо для меня.

В моем решении используется Spring FactoryBean, но он использует только один factory bean для создания всех моих beans (которые, по-видимому, желал первоначальный запрос).

Мое решение состояло в том, чтобы реализовать factory -of-factories meta- factory, который обслуживает FactoryBean обертки вокруг реального, одиночного factory.

Вот Java для моего JMockit mock bean factory:

public class MockBeanFactory<C> implements FactoryBean<C> {

    private Class<C> mockBeanType;
    protected MockBeanFactory(){}

    protected  <C> C create(Class<C> mockClass) {
        return Mockit.newEmptyProxy(mockClass);
    }

    @Override
    public C getObject() throws Exception {
        return create(mockBeanType);
    }

    @Override
    public Class<C> getObjectType() {
        return mockBeanType;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }

    public static class MetaFactory {
        public <C> MockBeanFactory<C> createFactory(Class<C> mockBeanType) {
            MockBeanFactory<C> factory = new MockBeanFactory<C>();
            factory.mockBeanType = mockBeanType;
            return factory;
        }
    }
}

И затем в XML файле контекста Spring вы просто можете просто создать мета factory, который создает конкретные bean -типы:

<bean id="metaFactory" class="com.stackoverflow.MockBeanFactory$MetaFactory"/>

<bean factory-bean="metaFactory" factory-method="createFactory">
    <constructor-arg name="mockBeanType" value="com.stackoverflow.YourService"/>
</bean>

Чтобы сделать эту работу для исходной ситуации с опросом, ее можно было бы настроить, чтобы сделать FactoryBeans в wrappers/adapter для serviceFactoryBean:

public class FancyFactoryAdapter<C> implements FactoryBean<C> {

    private Class<C> clientClass;
    private FancyFactory serviceFactoryBean;

    protected FancyFactoryAdapter(){}

    @Override
    public C getObject() throws Exception {
        return serviceFactoryBean.buildService(clientClass);
    }

    @Override
    public Class<C> getObjectType() {
        return clientClass;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }

    public static class MetaFactory {

        @Autowired FancyFactory serviceFactoryBean;

        public <C> FancyFactoryAdapter<C> createFactory(Class<C> clientClass) {
            FancyFactoryAdapter<C> factory = new FancyFactoryAdapter<C>();
            factory.clientClass = clientClass;
            factory.serviceFactoryBean = serviceFactoryBean;
            return factory;
        }
    }
}

Затем в XML (обратите внимание, что идентификатор userServiceFactory и userService bean id необходимы только для работы с аннотацией @Qualifier):

<bean id="metaFactory" class="com.stackoverflow.FancyFactoryAdapter$MetaFactory"/>

<bean id="userServiceFactory" factory-bean="metaFactory" factory-method="createFactory">
    <constructor-arg name="clientClass" value="com.captain.services.UserServiceInterface"/>
</bean>

<bean id="userService" factory-bean="userServiceFactory"/>

<bean id="scoreServiceFactory" factory-bean="metaFactory" factory-method="createFactory">
    <constructor-arg name="clientClass" value="com.captain.services.ScoreServiceInterface"/>
</bean>

<bean id="scoreService" factory-bean="scoreServiceFactory"/>

И что он, только один маленький класс Java и smidge конфигурации котельной плиты и ваш пользовательский bean factory могут создать все ваши beans и иметь Spring успешно разрешить их.

Ответ 3

Вы сможете достичь этого, используя:

<bean id="myCreatedObjectBean" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
    <property name="targetClass">
        <value>com.mycompany.MyFactoryClass</value>
    </property>
    <property name="targetMethod">
        <value>myFactoryMethod</value>
    </property>
</bean>

Затем вы можете использовать либо @Resource, либо @Autowired + @Qualifier для непосредственного ввода в ваш объект.