[英]How to force CDI/Weld to work with the new keyword?
我有一个命令行Java SE应用程序,我想对其进行现代化。 我想在其他CDI功能中使用拦截器和依赖项注入。 但是,该应用程序在设计时并未考虑CDI或依赖项注入,而是广泛使用new关键字和构造函数参数,而不是将对象创建委托给DI容器。 CDI / Weld不会在使用new创建的对象上注入依赖项或运行拦截器,并且它根本无法处理构造函数参数。 一个简化的例子:
class Main {
@Inject
private SomeModule someModule;
public static void main (String[] args) {
SeContainer container = ... set up CDI container ...
Main main = container.select(Main.class).get();
main.main(args);
}
@TraceLog
public Main () {
...
}
@TraceLog
public main (String[] args) {
Encryptor = new Encryptor(args[1], args[2], args[3]);
encryptor.run();
}
}
class Encryptor {
@Inject
private SomeModule someModule;
private String inputFile;
private String outputFile;
private String key;
@TraceLog
public Encryptor (String inputFile, String outputFile, String key) {
...
}
@TraceLog
public run () {
...
}
}
Main由CDI容器实例化,注入someModule,并为构造函数和方法调用@TraceLog拦截器。 但是,将使用new关键字显式创建Encryptor,不会注入someModule,并且不会调用@TraceLog。
CDI支持以编程方式创建bean,但仅适用于具有无参数非私有构造函数的类。 例子:
CDI.current().select(DefinitelyNotEncryptor.class).get();
@Inject
private Instance<DefinitelyNotEncryptor> instance;
instance.select(DefinitelyNotEncryptor.class).get();
Spring支持使用AspectJ注入到使用new关键字创建的对象中 。 尽管不知道在构造函数和方法上支持拦截器。
@Configurable(preConstruction = true)
@Component
class Encryptor {
@Autowired
private SomeModule someModule;
private String inputFile;
private String outputFile;
private String key;
@TraceLog
public Encryptor (String inputFile, String outputFile, String key) {
...
}
@TraceLog
public run () {
...
}
}
CDI / Weld是否有任何类似的解决方案? 还是应该使用Spring? 它是否支持构造函数和方法拦截器?
让我们从几句话开始...
它根本无法处理构造函数参数
错误。 这称为构造函数注入 。唯一的限制是所有参数必须是可解析的CDI bean。
@Inject
public Foo(Bar bar) { // -> CDI will attempt to inject Bar
// constructor logic
}
CDI / Weld不会在使用new创建的对象上注入依赖项或运行拦截器
是的,默认情况下不是。 但是可以通过BeanManager.createInjectionTarget(...).inject(...)
实现,但是这不是转换现有应用程序的首选方法!
注意:上面的代码将仅启用注入。 不拦截。 为此,可能需要使用InterceptionFactory
。 不过,您也不需要解决任何一个问题。
CDI支持以编程方式创建bean ...
用代码( Instance<T>
)描述的不是创建而是动态/程序化查找 。 它遵循与@Inject
相同的分辨率规则,仅允许您使其动态而不固定。 如果说创造,您可能是指生产者方法 ?
现在,解决您的问题...如果我正确理解,唯一的问题是Encryptor
的构造函数具有参数。 好吧,那么您需要确保可以以某种方式注入那些参数。 由于它们都是String
类型的三个,因此您将需要将它们包装在某些bean中或使用限定符,以便当您有多个String
类型的bean时, typesafe解析不会因模棱两可的解析而String
。
这是基于限定符的解决方案的构造函数的外观。 @Output
, @Input
和@Key
都是限定词 :
@Inject
public Encryptor (@Input String inputFile, @Output String outputFile, @Key String key){...}
这是这些限定词之一的示例:
@Qualifier
@Retention(RUNTIME)
@Target({METHOD, FIELD, PARAMETER, TYPE})
public @interface Key {}
最后,您需要产生可以用上述生产者方法完成的String
豆。 这是一个(获取该值的逻辑除外,因为我不知道您是怎么做到的):
@Produces
@Key
public String produceKeyString() {
// CDI will invoke this method in order to create bean of type String with qual. @Key
String key = new String("safeKey") // replace with your logic to get the value
return key;
}
如果要进行CDI注入,则必须避免使用new
运算符。
这就是我如何找到您的设计问题的解决方案
@ApplicationScoped
class Main
{
@Inject
private SomeModule someModule;
@Inject
private Encryptor encryptor;
public static void main (String[] args)
{
SeContainer container = ... set up CDI container ...
Main main = container.select(Main.class).get();
main.run(args);
}
@TraceLog
public Main ()
{
...
}
@TraceLog
public void run(String[] args)
{
encryptor.init(args[0], args[1], args[2]).run();
}
}
@Dependent
class Encryptor
{
@Inject
private SomeModule someModule;
private String inputFile;
private String outputFile;
private String key;
@TraceLog
public Encryptor init(String inputFile, String outputFile, String key)
{
this.inputFile = inputFile;
this.outputFile = outputFile;
this.key = key;
return this;
}
protected void run()
{
// do the real job of the encryptor here
}
}
关于此代码的主要内容是:
ApplicationScoped
和Dependent
Encryptor
类上提供init()
方法,该方法将使用运行时参数 init()
的末尾返回Encryptor的实例,以能够调用run()
方法,该方法受保护以避免直接调用(我认为也可以是private
),而无需先调用init()
它应与此设计一起使用。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.