具有CDI的工厂模式取决于运行时参数

时间:2013-06-06 06:46:49

标签: cdi factory-pattern

我想用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的相同实例。这样就不会消耗内存。 但是当我有可能更优雅地解决正确的实现时,我不喜欢它迭代一个集合。

您有什么想法可以解决问题吗?否则我可能不得不保留迭代解决方案。

2 个答案:

答案 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
}
}