java Spring 3 中的自定义 Autowire 候选 bean
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/14992796/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Custom Autowire candidate beans in Spring 3
提问by Strelok
Say I have the following structure with a service interface ServiceInterface
and a couple of components implementing it: ProductAService
and ProductBService
I also have a RequestContext
bean that has a qualifying property that says that we're say currently processing ProductA or ProductB. How can then automatically inject with autowiring or other annotation the correct implementation (ProductAService or ProductBService) into some service that needs it (ServiceThatNeedsServiceInterface
below).
说我有一个服务接口下面的结构ServiceInterface
和实现它几个部分组成:ProductAService
和ProductBService
我也有一个RequestContext
,有一个合格的属性,说,我们说目前正在处理产品A产品B或豆。然后如何使用自动装配或其他注释将正确的实现(ProductAService 或 ProductBService)自动注入到需要它的某些服务中(ServiceThatNeedsServiceInterface
如下)。
public interface ServiceInterface {
void someMethod();
}
@Component(name="ProductAService")
public class ProductAService implements ServiceInterface {
@Override public void someMethod() {
System.out.println("Hello, A Service");
}
}
@Component(name="ProductBService")
public class ProductBService implements ServiceInterface {
@Override public void someMethod() {
System.out.println("Hello, B Service");
}
}
@Component
public class ServiceThatNeedsServiceInterface {
// What to do here???
@Autowired
ServiceInterface service;
public void useService() {
service.someMethod();
}
}
@Component
@Scope( value = WebApplicationContext.SCOPE_REQUEST )
public class RequestContext {
String getSomeQualifierProperty();
}
回答by Haim Raman
Spring Source were referring to your issue when they created the ServiceLocatorFactoryBeanback in version 1.1.4. In order to use it you need to add an interface similar to the one below:
Spring Source在 1.1.4 版中创建ServiceLocatorFactoryBean时指的是您的问题。为了使用它,您需要添加一个类似于下面的界面:
public interface ServiceLocator {
//ServiceInterface service name is the one
//set by @Component
public ServiceInterface lookup(String serviceName);
}
You need to add the following snippet to your applicationContext.xml
您需要将以下代码段添加到 applicationContext.xml
<bean id="serviceLocatorFactoryBean"
class="org.springframework.beans.factory.config.ServiceLocatorFactoryBean">
<property name="serviceLocatorInterface"
value="org.haim.springframwork.stackoverflow.ServiceLocator" />
</bean>
Now your ServiceThatNeedsServiceInterface will look similar to the one below:
现在您的 ServiceThatNeedsServiceInterface 将类似于以下内容:
@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();
}
}
ServiceLocatorFactoryBean will return the desired service based on the RequestContext qualifier. Apart from spring annotations your code is not depended on Spring. I executed the following unit test for the above
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();
}
}
The console will display
Hello, A Service
Hello, B Service
控制台会显示
你好,A 服务
你好,B 服务
A word of warning. The API documentation states that
“Such service locators … will typically be used for prototype beans, i.e. for factory methods that are supposed to return a new instance for each call… For singleton beans, direct setter or constructor injection of the target bean is preferable.”
一句警告。API 文档指出
“此类服务定位器……通常用于原型 bean,即用于应该为每次调用返回一个新实例的工厂方法……对于单例 bean,目标 bean 的直接 setter 或构造函数注入是更可取的。 ”
I cannot understand why this may cause an issue. In my code it returns the same service on two sequence calls to serviceThatNeedsServiceInterface.useService();
我不明白为什么这可能会导致问题。在我的代码中,它在对 serviceThatNeedsServiceInterface.useService() 的两次序列调用中返回相同的服务;
You can find the source code for my example in GitHub
您可以在GitHub 中找到我的示例的源代码
回答by Ryan Stewart
The only way I can think to do something like what you're looking for is to create something like a FactoryBean that returns the appropriate implementation based on the RequestContext property. Here's something I slapped together that has the behavior you want:
我能想到的做类似您正在寻找的事情的唯一方法是创建类似 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");
}
}
}
I've put a working example using this code on Github. You can clone and run it with:
我在 Github 上放了一个使用此代码的工作示例。您可以使用以下命令克隆并运行它:
git clone git://github.com/zzantozz/testbed tmp
cd tmp/spring-mvc
mvn jetty:run
Then visit http://localhost:8080/dynamicallyInjected
to see the result of one dependency, and http://localhost:8080/dynamicallyInjected?which=bob
to see the other.
然后访问http://localhost:8080/dynamicallyInjected
以查看一个依赖项的结果,并http://localhost:8080/dynamicallyInjected?which=bob
查看另一个。
回答by mukund
This may help you:
这可能会帮助您:
Use
利用
AutowireCapeableBeanFactory.autowireBean(Object existingBean)
OR
或者
AutowireCapeableBeanFactory.autowireBeanProperties(Object existingBean, int autowireMode, boolean dependencyCheck)
AutowireCapeableBeanFactory.autowireBean(Object existingBean)AutowireCapeableBeanFactory.autowireBeanProperties(Object existingBean, int autowireMode, boolean dependencyCheck)
AutowireCapeableBeanFactory.autowireBean(Object existingBean) AutowireCapeableBeanFactory.autowireBeanProperties(Object existingBean, int autowireMode, boolean dependencyCheck)
回答by PAcan
I guess, that you've missed the annotation, that tells spring, that you have a custom service. So your solution is to add this annotation before the class name:
我猜,你错过了注释,告诉 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");
}
}
And then you can auto wire it, but in order to use the specific service, you have to add the annotation Qualifier() like this:
然后你可以自动连接它,但为了使用特定的服务,你必须像这样添加注释 Qualifier():
@Autowired
@Qualifier("ProductBService") // or ProductAService
ServiceInterface service;
Or maybe you have to add just an annotation Qualifier("name of your bean") :)
或者您可能只需要添加一个注释限定符(“您的 bean 的名称”):)
回答by Elbek
I do not think you can do this with annotation, the reason is you need a bean that is dynamic on runtime(maybe A service or B service), so @Autowire will be wired before the bean is being used in any place. One solution is get bean from context when you need.
我不认为你可以用注释来做到这一点,原因是你需要一个在运行时动态的 bean(可能是 A 服务或 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();
}
}
You can put is else logic somewhere in the class as a seperate function:
您可以将其他逻辑作为单独的函数放在类中的某处:
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);
}
}
This is dynamic and this might be what you want.
这是动态的,这可能是您想要的。
回答by wrm
You could use the @Qualifier annotation in combination with aliases. See an example of how it is used to load a bean based on a property here. You could modify this approach and change the property/alias in the requestcontext...
您可以将 @Qualifier 注释与别名结合使用。在此处查看有关如何使用它基于属性加载 bean 的示例。您可以修改此方法并更改请求上下文中的属性/别名...