Java Spring3中的自定义Autowire候选bean
假设我有一个服务接口Java Spring3中的自定义Autowire候选bean,java,spring,dependency-injection,annotations,Java,Spring,Dependency Injection,Annotations,假设我有一个服务接口ServiceInterface和几个实现它的组件:ProductAService和ProductBService我还有一个RequestContextbean,它有一个限定属性,表示我们正在处理ProductA或ProductB。然后,如何使用自动连接或其他注释将正确的实现(ProductAService或ProductBService)自动注入到需要它的服务中(需要服务接口的服务) 我猜,您错过了注释,它告诉spring,您有一个定制服务。 因此,您的解决方案是在类名之前
ServiceInterface
和几个实现它的组件:ProductAService
和ProductBService
我还有一个RequestContext
bean,它有一个限定属性,表示我们正在处理ProductA或ProductB。然后,如何使用自动连接或其他注释将正确的实现(ProductAService或ProductBService)自动注入到需要它的服务中(需要服务接口的服务
)
我猜,您错过了注释,它告诉spring,您有一个定制服务。 因此,您的解决方案是在类名之前添加此注释:
@Service("ProductAService")
public class ProductAService implements ServiceInterface {
@Override public void someMethod() {
System.out.println("Hello, A Service");
}
}
@Service("ProductBService")
public class ProductBService implements ServiceInterface {
@Override public void someMethod() {
System.out.println("Hello, B Service");
}
}
然后您可以自动连接它,但为了使用特定的服务,您必须添加注释限定符(),如下所示:
@Autowired
@Qualifier("ProductBService") // or ProductAService
ServiceInterface service;
或者你可能只需要添加一个注释限定符(“你的bean的名称”):)我认为你不能用注释来完成这项工作,原因是你需要一个在运行时是动态的bean(可能是一个服务或B服务),所以@Autowire将在bean在任何地方使用之前进行连接。一种解决方案是在需要时从上下文获取bean
@Component
public class ServiceThatNeedsServiceInterface {
ServiceInterface service;
public void useService() {
if(something is something){
service = applicationContext.getBean("Abean", ServiceInterface.class);
}else{
service = applicationContext.getBean("Bbean", ServiceInterface.class);
}
service.someMethod();
}
}
您可以将is else逻辑作为单独的函数放在类中的某个位置:
public void useService() {
service = findService();
service.someMethod();
}
public ServiceInterface findService() {
if(something is something){
return applicationContext.getBean("Abean", ServiceInterface.class);
}else{
return applicationContext.getBean("Bbean", ServiceInterface.class);
}
}
这是动态的,这可能是你想要的 我能想到的唯一方法是创建类似FactoryBean的东西,它根据RequestContext属性返回适当的实现。下面是我拼凑的一些东西,它有你想要的行为:
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.WebApplicationContext;
import javax.servlet.http.HttpServletRequest;
public class InjectionQualifiedByProperty {
@Controller
@Scope(WebApplicationContext.SCOPE_REQUEST)
public static class DynamicallyInjectedController {
@Autowired
@Qualifier("picker")
Dependency dependency;
@RequestMapping(value = "/sayHi", method = RequestMethod.GET)
@ResponseBody
public String sayHi() {
return dependency.sayHi();
}
}
public interface Dependency {
String sayHi();
}
@Configuration
public static class Beans {
@Bean
@Scope(WebApplicationContext.SCOPE_REQUEST)
@Qualifier("picker")
FactoryBean<Dependency> dependencyPicker(final RequestContext requestContext,
final BobDependency bob, final FredDependency fred) {
return new FactoryBean<Dependency>() {
@Override
public Dependency getObject() throws Exception {
if ("bob".equals(requestContext.getQualifierProperty())) {
return bob;
} else {
return fred;
}
}
@Override
public Class<?> getObjectType() {
return Dependency.class;
}
@Override
public boolean isSingleton() {
return false;
}
};
}
}
@Component
public static class BobDependency implements Dependency {
@Override
public String sayHi() {
return "Hi, I'm Bob";
}
}
@Component
public static class FredDependency implements Dependency {
@Override
public String sayHi() {
return "I'm not Bob";
}
}
@Component
@Scope(WebApplicationContext.SCOPE_REQUEST)
public static class RequestContext {
@Autowired HttpServletRequest request;
String getQualifierProperty() {
return request.getParameter("which");
}
}
}
然后访问http://localhost:8080/dynamicallyInjected
查看一个依赖项的结果,以及http://localhost:8080/dynamicallyInjected?which=bob
查看另一个。这可能会帮助您:
使用
或
您可以将@Qualifier注释与别名结合使用。请参见如何使用它基于属性加载bean的示例。您可以修改此方法并更改requestcontext中的属性/别名 Spring Source在版本1.1.4中创建back时,提到了您的问题。为了使用它,您需要添加一个类似于以下界面的界面:
public interface ServiceLocator {
//ServiceInterface service name is the one
//set by @Component
public ServiceInterface lookup(String serviceName);
}
@Component
public class ServiceThatNeedsServiceInterface {
// What to do here???
// @Autowired
// ServiceInterface service;
/*
* ServiceLocator lookup returns the desired implementation
* (ProductAService or ProductBService)
*/
@Autowired
private ServiceLocator serviceLocatorFactoryBean;
//Let’s assume we got this from the web request
public RequestContext context;
public void useService() {
ServiceInterface service =
serviceLocatorFactoryBean.lookup(context.getQualifier());
service.someMethod();
}
}
您需要将以下代码段添加到applicationContext.xml中
<bean id="serviceLocatorFactoryBean"
class="org.springframework.beans.factory.config.ServiceLocatorFactoryBean">
<property name="serviceLocatorInterface"
value="org.haim.springframwork.stackoverflow.ServiceLocator" />
</bean>
ServiceLocatorFactoryBean将基于RequestContext限定符返回所需的服务。
除了spring注释之外,您的代码并不依赖于spring。
我对上面的代码执行了以下单元测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:META-INF/spring/applicationContext.xml" })
public class ServiceThatNeedsServiceInterfaceTest {
@Autowired
ServiceThatNeedsServiceInterface serviceThatNeedsServiceInterface;
@Test
public void testUseService() {
//As we are not running from a web container
//so we set the context directly to the service
RequestContext context = new RequestContext();
context.setQualifier("ProductAService");
serviceThatNeedsServiceInterface.context = context;
serviceThatNeedsServiceInterface.useService();
context.setQualifier("ProductBService");
serviceThatNeedsServiceInterface.context = context;
serviceThatNeedsServiceInterface.useService();
}
}
控制台将显示您好,是一项服务
你好,B服务 一句警告的话。API文档说明
“此类服务定位器……通常用于原型bean,即用于工厂方法,该工厂方法应为每个调用返回一个新实例……对于单例bean,最好是直接插入目标bean的setter或构造函数。” 我不明白这为什么会引起问题。在我的代码中,它在对需要ServiceInterface.useService()的服务的两个序列调用中返回相同的服务
您可以在中找到我的示例的源代码,我知道我可以做到这一点,但我必须在使用它的类中将所有不同的服务作为单独的字段。我希望RequestContext中的属性成为自动连接时要注入的“产品”实现的决定因素。宾果!这是正确的答案。我不介意一点点XML配置。
public interface ServiceLocator {
//ServiceInterface service name is the one
//set by @Component
public ServiceInterface lookup(String serviceName);
}
<bean id="serviceLocatorFactoryBean"
class="org.springframework.beans.factory.config.ServiceLocatorFactoryBean">
<property name="serviceLocatorInterface"
value="org.haim.springframwork.stackoverflow.ServiceLocator" />
</bean>
@Component
public class ServiceThatNeedsServiceInterface {
// What to do here???
// @Autowired
// ServiceInterface service;
/*
* ServiceLocator lookup returns the desired implementation
* (ProductAService or ProductBService)
*/
@Autowired
private ServiceLocator serviceLocatorFactoryBean;
//Let’s assume we got this from the web request
public RequestContext context;
public void useService() {
ServiceInterface service =
serviceLocatorFactoryBean.lookup(context.getQualifier());
service.someMethod();
}
}
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:META-INF/spring/applicationContext.xml" })
public class ServiceThatNeedsServiceInterfaceTest {
@Autowired
ServiceThatNeedsServiceInterface serviceThatNeedsServiceInterface;
@Test
public void testUseService() {
//As we are not running from a web container
//so we set the context directly to the service
RequestContext context = new RequestContext();
context.setQualifier("ProductAService");
serviceThatNeedsServiceInterface.context = context;
serviceThatNeedsServiceInterface.useService();
context.setQualifier("ProductBService");
serviceThatNeedsServiceInterface.context = context;
serviceThatNeedsServiceInterface.useService();
}
}