我有两个bean 来实现接口:
@Storage(StorageType.LOCAL)
public class LocalStorage implements StorageService {
// [...]
}
和
@Storage(StorageType.REMOTE)
public class RemoteStorage implements StorageService {
// [...]
}
在服务类中,我使用的是注入的StorageService :
@Stateless
public class DocumentService {
@Inject
@Storage(StorageType.REMOTE)
private StorageService storageService;
// [...]
}
这很有效,但我希望能够从外部配置StorageType ,而无需更改源代码。
所以我创建了一个制作人:
@Singleton
public class StorageServiceProducer {
@Inject
@ConfigurationValue("storage.type") // Injects values from a properties file
private String storageType;
@Produces
public StorageService produceStorageService(InjectionPoint injectionPoint) {
if (storageType.equals("remote")) {
return new RemoteStorage();
} else {
return new LocalStorage();
}
}
}
...并从我的bean中删除了@Storage注释:
public class LocalStorage implements StorageService {
// [...]
}
和
public class RemoteStorage implements StorageService {
// [...]
}
但是现在我得到了一个模糊的依赖异常,大概是因为生产者本身的存在。为了强制使用生产者,我发现我可以使用“@Vetoed”注释。
这似乎有用,但由于bean不再受管理,我的实现中的任何注入值都缺失:
public class RemoteStorage implements StorageService {
@Inject
@ConfigurationValue("project.id")
private String projectId;
// [...]
}
所以这是我的问题:
编辑:我正在使用CDI 1.2和bean-discovery-mode =“all”
答案 0 :(得分:3)
对于动态制作人来说,这是一种正确的方法,但请注意,CDI通常是非常静态的,因此尝试动态制作内容可能并不总是让人感觉顺畅。
现在,您要碰到的是您自己创建实例(使用new
),因此CDI只知道生成的对象并且无法控制它。例如。它不会注入你的impl bean的fields / constructor / ..
我可以从头脑中想到两个选项:
这意味着您不会自己创建bean,而您的生产者只会移交正确的bean。这种方法的好处在于CDI可以处理所有这些,因此您的注射将无需任何额外的努力即可工作。此外,拦截,装饰,事件处理程序等都将按预期工作。不那么光明的一面是,如果你有更多的实现,或者随着时间的推移会增加更多,那么这会变得混乱。
为实现这一目标,您可以(例如)限制类型的实施,以便在您@Inject StorageService
时不适合。
@Typed({Object.class, RemoteStorage.class})
public class RemoteStorage implements StorageService { ....}
并为其他存储实现做同样的事情。 然后,从你的制作人那里,你可以做这样的事情:
@ApplicationScoped
public class StorageServiceProducer {
@Inject
@ConfigurationValue("storage.type") // Injects values from a properties file
private String storageType;
// there should be no ambiguity injecting specific impl type
@Inject
RemoteStorage remoteStorage;
@Inject
LocalStorage localStorage;
@Produces
public StorageService produceStorageService(InjectionPoint injectionPoint) {
if (storageType.equals("remote")) {
return remoteStorage;
} else {
return localStorage;
}
}
}
在这种方法中,我们保持您的代码按原样,只添加一个片段,它将注入到生成的实例中。好处是,它可能更容易扩展,而且非常简单。缺点是你没有得到一个成熟的CDI bean,你只是把一个实例交给CDI说" 嘿,如果有人要求提供类型' StorageService'的bean,你给他这个,哦,请注入它。"拦截,装饰,观察员和其他东西都不会在那里工作。更准确地说,您只需将该实例转换为CDI调用的InjectionTarget
。
以下是:
@ApplicationScoped
public class StorageServiceProducer {
@Inject
@ConfigurationValue("storage.type") // Injects values from a properties file
private String storageType;
@Inject
BeanManager bm;
@Produces
public StorageService produceStorageService(InjectionPoint injectionPoint) {
StorageService result = null;
if (storageType.equals("remote")) {
result = RemoteStorage();
} else {
result = LocalStorage();
}
// make it an injection target, it's gonna be something like this
CreationalContext<Object> ctx = bm.createCreationalContext(null);
InjectionTarget<Object> injectionTarget = (InjectionTarget<Object>) beanManager
.getInjectionTargetFactory(bm.createAnnotatedType(result.getClass())).createInjectionTarget(null);
injectionTarget.inject(result, ctx);
// return result which was injected into
return result;
}
}
答案 1 :(得分:0)
一个简单的解决方案是使用@New限定符。
首先,您需要从自动发现中排除两个bean实现。不确定您正在运行的CDI版本 - 从1.1您可以使用@Vetoed注释。或者在这个答案中使用beans.xml:How to exclude a class from scanning with CDI 1.0(虽然它是特定的Weld)
@Vetoed
public class LocalStorage implements StorageService {
// [...]
}
@Vetoed
public class RemoteStorage implements StorageService {
// [...]
}
然后使用@New限定符将两个实现注入到producer方法中:
@Singleton
public class StorageServiceProducer {
@Inject
@ConfigurationValue("storage.type") // Injects values from a properties file
private String storageType;
@Produces
public StorageService produceStorageService(@New LocalStorage localStorage, @New RemoteStorage remoteStorage) {
if (storageType.equals("remote")) {
return remoteStorage;
} else {
return localStorage;
}
}
}
答案 2 :(得分:0)
我认为您可以像现在一样使用两个Storage
实现,但删除限定符并非常明确地键入它们以排除StorageService
(这也假设它们已被添加到CDI中系统作为已启用的bean通过一种或另一种机制启用,例如,如果它们也使用bean-defining annotation或programmatically added by an extension进行注释:
@Typed(LocalStorage.class)
public class LocalStorage implements StorageService {
// [...]
}
@Typed(RemoteStorage.class)
public class RemoteStorage implements StorageService {
// [...]
}
接下来,让它们被注入生产者方法的依赖项:
@Produces
public StorageService produceStorageService(LocalStorage localStorage, RemoteStorage remoteStorage) {
if (storageType.equals("remote")) {
return remoteStorage;
} else {
return localStorage;
}
}