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 для непосредственного ввода в ваш объект.