>我的应用程序当前使用一个类,其中两个服务注入了@Inject
注释。
@Stateless
public class MyClass {
@Inject
private SomeService someService;
@Inject
private OtherService otherService;
}
这两种服务非常相似,并且都扩展了抽象Service
类。
这是我正在尝试做的...
我的基本想法是MyClass
类看起来像这样:
@Stateless
public class MyClass {
@Inject
private Service service;
}
根据配置,应用程序决定注入SomeService
或OtherService
例:
if (config.getValue().equals("some_service")) {
return new SomeService();
} else if (config.getValue().equals("other_service")) {
return new OtherService();
}
Jave EE是否为此提供了解决方案?
要使这项工作,您需要确保任何"制造"SomeService
从它可以创建的类型列表中消除Service
,以及任何"制造"OtherService
从它可以创建的类型列表中消除Service
。
例如,如果SomeService
是一个简单的受管 Bean,则需要向其添加@Typed(SomeService.class)
注释:
@Typed(SomeService.class)
public class SomeService extends Service {
}
另一方面,如果SomeService
是由生产者方法生成的,则必须类似地执行相同的操作:
@Produces
@ApplicationScoped
@Typed(SomeService.class)
private SomeService makeSomeService() {
return fabricateSomeService();
}
@Typed
注释将类型集限制为给定的任何内容,而不是推断的内容。
如果您在两个"具体"服务上都执行此操作,那么您在上面的答案中编写的getService()
生产者方法应该可以工作。
你的意思是:"取决于配置..."您何时决定使用什么?在编译时还是运行时?
有几种方法可以完成此操作:
1.@Alternative豆.xml
用@Alternative注释某些服务和其他服务,并在 bean 中再次激活其中一个.xml
<beans xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
<alternatives>
<class>SomeService</class>
</alternatives>
</beans>
2. 与限定符和生产者:
@Qualifier
@Retention(RUNTIME)
@Target({TYPE, METHOD, FIELD, PARAMETER})
public @interface First {}
并用以下方法注释两个 Beans:
@First
@Stateless
public SomeService{ ... }
现在,您可以拥有一个如下所示的生产者类:
@Dependent
public class ServiceProducer {
@Inject
@First
private Service someService;
@Inject
@Second
private Service otherService;
@Produces
@Default
public Service getService() {
switch (someCondition) {
case SOME:
return someService;
case OTHER:
return otherService;
default:
return null;
}
}
}
最后将服务注入到您想要使用它的位置:
@Inject
Service service;
3. 没有生产者,但有限定符注释
您需要注释某些服务和其他服务才能使其正常工作。
@Inject
Instance<Service> services;
public void someBussinessMethod(){
Annotation qualifier = someCondition ? new AnnotationLiteral<First>() {} : new AnnotationLiteral<Second>() {};
Service s = services.select(qualifier).get();
}
4. 不带限定符
在我看来,这是最丑陋和最慢的解决方案,但您可以迭代 Injectend 服务并由类决定是否要使用它。
@Inject
Instance<Service> services;
public void doSomething() {
Class clazz = someCondition ? SomeService.class : OtherService.class;
Service first = services.stream().filter(s -> s.getClass() == clazz).findFirst().get();
}
详细的说明可以在这里找到: https://docs.jboss.org/cdi/learn/userguide/CDI-user-guide.html#injection
关于@kukeltje的评论,我使用了这样的@Produces
注释:
@ApplicationScoped
public class MyProducer {
@Inject
private SomeService someService;
@Inject
private OtherService otherService;
@Produces
@ApplicationScoped
public Service getService() {
switch (someCondition) {
case SOME:
return someService;
case OTHER:
return otherService;
default:
return null;
}
}
}
用法:
@Stateless
public class MyClass {
@Inject
private Service service;
}