4

我想用 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

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

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

4

2 回答 2

1

这就是你的问题。Instance 保持对您从中获取的实例的引用,get()因为它负责在它们超出范围时回收它们(即当注入Instance超出范围时。但是因为您使您的工厂成为单例,所以它永远不会超出范围。所以,让你的工厂成为一个短命的范围,就像@RequestScoped或什至一样@Dependent,这样所有返回的实例都将被正确回收。

于 2014-03-17T10:01:14.667 回答
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
}
}
于 2014-07-09T14:35:02.077 回答