我想用CDI实现工厂模式。这里我们有商业案例:
客户端提供表示类型的字符串。根据此类型,工厂将返回接口的实现。
我知道有很多关于工厂模式和CDI的问题。我在这里的不同之处在于我根据运行时参数解析了工厂返回的实现。
我正在考虑使用生产者方法,但后来我想不出如何将已解析的实现注入到需要实现的bean中,因为这是一个运行时参数,在构造时不一定知道。
所以我想到了使用Instance类的非常直接的方法。
以下是基本实现:
// the interface. Instances of this class are returned from the factory
public interface Product {
}
// one implementation may be returned by the factory
@ProductType("default")
public class DefaultProduct implements Product {
}
// another implementation may be returned by the factory
@ProductType("myProduct")
public class MyProduct implements Product {
}
// the qualifier annotation
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.TYPE})
public @interface ProductType {
String value();
}
// the Annotation implementation to select
// the correct implementation in the factory
public class ProductTypeLiteral extends AnnotationLiteral<ProductType>
implements ProductType {
private String type;
public ProductTypeLiteral(String type) {
this.type = type;
}
@Override
public String value() {
return type;
}
}
// the factory itself. It is annotated with @Singleton because the
// factory is only needed once
@Singleton
public class Factory {
@Inject
@Any
private Instance<Product> products;
public Product getProduct(String type) {
ProductTypeLiteral literal = new ProductTypeLiteral(type);
Instance<Product> typeProducts = products.select(literal);
return typeProducts.get();
}
}
在我看来,使用Instance是非常复杂的。
但这有一个主要缺点:
每次调用Instance.get()
方法时,都会检索Product
的新实例。这可能没问题,但Instance
实例在内部保留了返回实例的引用。因此,只要Factory
存在,并且每次调用Instance.get()
时,内存中都会存在Product
的更多实例,并且永远不会收集垃圾,因为Instance
中的引用仍然存在1}}。
我想过不要让Factory
成为一个单身人士,但这只会改变问题并且无法解决问题。当然这是违反工厂模式的。
我尝试的另一个解决方案是迭代Instance
,而不是在注释的帮助下选择实现:
@Singleton
public class Factory {
@Inject
@Any
private Instance<Product> products;
public Product getProduct(String type) {
Product product = null;
for(Product eachProduct : products) {
ProductType productType = eachProduct.getClass().
getAnnotation(ProductType.class)
if(productType.value().equals(type) {
product = eachProduct;
break;
}
}
return product;
}
}
基本上这是有效的。现在每次都取决于给定的类型,我检索Product
的相同实例。这样就不会消耗内存。
但是当我有可能更优雅地解决正确的实现时,我不喜欢它迭代一个集合。
您有什么想法可以解决问题吗?否则我可能不得不保留迭代解决方案。
答案 0 :(得分:1)
这就是你的问题所在。实例保留对使用get()
从中获取的实例的引用,因为当它们超出范围时(即注入的Instance
超出范围时,它负责回收它们。)但是因为你创建了工厂单身,它永远不会超出范围。因此,使您的工厂成为短暂的范围,如@RequestScoped
甚至@Dependent
,这样所有返回的实例都将被正确回收。
答案 1 :(得分:0)
也许它可以帮到你:
创建限定符:
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE})
public @interface MyProduct{
}
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE})
public @interface DefaultProduct{
}
在工厂类中:
@Singleton
public class Factory {
public Product getProduct(@MyProduct MyProduct product, @DefaultProduct DefaultProduct defaultProduct) {
//What you wanna do
}
}