首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何创建可配置的CDI生产者?

如何创建可配置的CDI生产者?
EN

Stack Overflow用户
提问于 2018-01-10 13:58:02
回答 3查看 1.6K关注 0票数 1

我有两bean,它实现了接口

代码语言:javascript
复制
@Storage(StorageType.LOCAL)
public class LocalStorage implements StorageService { 
    // [...]
}

代码语言:javascript
复制
@Storage(StorageType.REMOTE)
public class RemoteStorage implements StorageService { 
    // [...]
}

在服务类中,我使用的是注入的StorageService

代码语言:javascript
复制
@Stateless
public class DocumentService {

    @Inject
    @Storage(StorageType.REMOTE)
    private StorageService storageService;

    // [...]

}

这很好,但是我希望能够在不改变源代码的情况下从外部配置StorageType。

所以我创造了一个制作人:

代码语言:javascript
复制
@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();
        }
    }
}

...and从我的bean中删除了@Storage注释:

代码语言:javascript
复制
public class LocalStorage implements StorageService { 
    // [...]
}

代码语言:javascript
复制
public class RemoteStorage implements StorageService { 
    // [...]
}

但是现在我得到了一个模糊依赖异常,大概是因为生产者本身的存在。为了强制生产者使用,我发现我可以使用"@Vetoed“注释。

这似乎是可行的,,但是由于bean不再被管理,所以我的实现中的任何注入值都缺少

代码语言:javascript
复制
public class RemoteStorage implements StorageService {

    @Inject
    @ConfigurationValue("project.id")
    private String projectId;

    // [...]

}

以下是我的问题:

  • 这是配置“动态”生产者的正确方法吗?
  • 我成功地使用了@Alternative,但这与注释方法有相同的缺点:如果我想更改实现,我需要更改beans.xml文件
  • 我怎样才能做到这一点?

编辑:我使用的是CDI 1.2和bean发现模式=“all”。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2018-01-10 15:13:38

这是一种对动态生产者的正确方法,但请注意,CDI通常是相当静态的,所以动态地尝试可能并不总是感觉顺利。现在,您遇到的是自己创建实例(使用new),因此CDI只知道结果对象,无法控制它。它不会注入到字段/构造函数/.你的豆子。

从我的头顶上我可以想到两种选择:

  1. 只对bean基进行操作。

这意味着您不会自己创建bean,而您的生产者只会交出正确的bean。这种方法的好处是CDI处理了所有的问题,因此您的注入将不需要任何额外的工作。此外,拦截、装饰、事件处理程序等都将如您所期望的那样工作。不好的一面是,如果你有更多的实现,或者随着时间的推移要增加更多的实现,那么事情就会变得一团糟。

要实现这一点,您可以(例如)限制您的实现的类型集,以便它们在您的@Inject StorageService时不适合。

代码语言:javascript
复制
@Typed({Object.class, RemoteStorage.class})
public class RemoteStorage implements StorageService { ....}

并对其他存储实现执行同样的操作。然后,从你的制片人那里,你可以这样做:

代码语言:javascript
复制
@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;
    }
}

}

  1. 自己创建对象并手动确保注入工作()

在这种方法中,我们保持代码的原样,只添加一个将注入结果实例的代码段。好处是,它可能更容易扩展,而且相当简单。缺点是你没有得到一个完整的CDI bean,你只是把一个实例交给CDI:“嘿,如果有人要一个'StorageService‘类型的bean,你就给他这个,哦,请注入它。”拦截,装饰,观察者和其他的东西不会在那里起作用。更准确地说,您只需将这个实例交给CDI所称的InjectionTarget

以下是如何:

代码语言:javascript
复制
@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;
}

}

票数 3
EN

Stack Overflow用户

发布于 2018-01-10 15:10:58

一个简单的解决方案是使用@New限定符。

首先,您需要从自动发现中排除这两个bean实现。不确定您正在运行哪个版本的CDI --从1.1开始,您可以使用@Vetoed注释。或者使用beans.xml来回答这个问题:如何用CDI1.0排除类的扫描 (虽然它是特定的)

代码语言:javascript
复制
@Vetoed
public class LocalStorage implements StorageService { 
// [...]
}

@Vetoed
public class RemoteStorage implements StorageService { 
    // [...]
}

然后使用@New限定符将这两个实现注入producer方法:

代码语言:javascript
复制
@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;
        }
    }
}
票数 0
EN

Stack Overflow用户

发布于 2018-05-04 20:39:29

我认为您可以像现在一样使用您的两个Storage实现,但是删除这些限定符并非常显式地键入它们以排除StorageService (这还假设它们已经通过某种机制以启用been的形式添加到CDI系统中,例如,如果它们也被用bean定义注释通过扩展以编程方式添加注释的话):

代码语言:javascript
复制
@Typed(LocalStorage.class)
public class LocalStorage implements StorageService { 
    // [...]
}

@Typed(RemoteStorage.class)
public class RemoteStorage implements StorageService { 
    // [...]
}

接下来,将它们注入到生产者方法的依赖项中:

代码语言:javascript
复制
@Produces
public StorageService produceStorageService(LocalStorage localStorage, RemoteStorage remoteStorage) {            
    if (storageType.equals("remote")) {
        return remoteStorage;
    } else {
        return localStorage;
    }
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/48189066

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档