我有一组带有复杂初始化方案的类。基本上,我从我需要获取的接口开始,然后进行一堆调用,最后得到一个实现该接口的对象。
为了处理这个问题,我创建了一个工厂类,它可以在给定接口的情况下生成最终对象。我将这个工厂变成了一个bean,并且在XML中我将各种服务bean指定为通过这个工厂对象实例化,并带有他们将实现的接口参数。
这很好用,我完全得到了我需要的豆子。不幸的是,我想从我的控制器类访问它们,这些类是通过组件扫描发现的。我在这里使用@Autowired,看起来Spring不知道它们是什么类型的对象,并且由于@Autowired按类型工作,我是SOL。
在这里使用@Resource(name =“beanName”)可以很好地工作,但是对某些bean使用@Resource而对其他bean使用@Autowired似乎很奇怪。
有没有办法让Spring了解工厂为每个bean创建的接口,而不为每种类型设置不同的工厂方法?
顺便说一句,我正在使用Spring 2.5.6,否则我只是将JavaConfig整个事情都忘掉了。
工厂类:
<T extends Client> T buildService(Class<T> clientClass) {
//Do lots of stuff with client class and return an object of clientClass.
}
app context:
<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;
}
答案 0 :(得分:8)
我建议您进一步采用工厂模式implement your factories as Spring FactoryBean
classes。 FactoryBean
接口有一个getObjectType()
方法,包含调用以发现工厂将返回的类型。只要您的工厂返回合理的价值,这就可以让您自动装配一些东西。
答案 1 :(得分:5)
我遇到了类似的问题,但对我来说,我想使用一个工厂来创建使用JMockit(我需要使用的测试框架)的自动连接依赖项的模拟实现。
在互联网上找不到令人满意的解决方案之后,我总结了一个对我来说非常好的简单解决方案。
我的解决方案也使用Spring FactoryBean
,但它只使用一个工厂bean来创建我的所有bean(原始提问者似乎希望这样做)。
我的解决方案是实施一个工厂工厂的元工厂,为真正的单一工厂提供FactoryBean
包装。
这是我的JMockit模拟bean工厂的Java:
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;
}
}
}
然后在Spring上下文XML文件中,您只需创建创建特定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
成为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
id和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类和一个很简单的样板配置,你的自定义bean工厂就可以创建所有的bean并让Spring成功解决它们。
答案 2 :(得分: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直接注入你的对象。