我有一个命令行 Java SE 应用程序,我想对其进行一些现代化改造。我想在其他 CDI 功能中使用拦截器和依赖项注入(inject)。然而,该应用程序在设计时并未考虑到 CDI 或依赖项注入(inject),它广泛使用 new 关键字和构造函数参数,而不是将对象创建委托(delegate)给 DI 容器。 CDI/Weld 不会在使用 new 创建的对象上注入(inject)依赖项或运行拦截器,并且它根本无法处理构造函数参数。一个简化的例子:
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 容器实例化,注入(inject) someModule,并为构造函数和方法调用 @TraceLog 拦截器。但是 Encryptor 是使用 new 关键字显式创建的,未注入(inject) someModule,并且未调用 @TraceLog。
CDI 支持以编程方式创建 Bean,但仅适用于具有无参数非私有(private)构造函数的类。示例:
CDI.current().select(DefinitelyNotEncryptor.class).get();
@Inject
private Instance<DefinitelyNotEncryptor> instance;
instance.select(DefinitelyNotEncryptor.class).get();
Spring supports injection into objects created with the new keyword, with the use of AspectJ 。但不知道构造函数和方法上对拦截器的支持。
@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?它支持构造函数和方法拦截器吗?
最佳答案
让我们先说几句话......
and it can not handle constructor parameters at all
错了。它的名字叫constructor injection唯一的限制是所有参数都必须是可解析的 CDI bean。
@Inject
public Foo(Bar bar) { // -> CDI will attempt to inject Bar
// constructor logic
}
CDI/Weld does not inject dependencies or run interceptors on objects created with new
是的,默认情况下不是。但这可以通过BeanManager.createInjectionTarget(...).inject(...)
来实现
但这不是转换现有应用程序的首选方法!
注意:以上代码仅启用注入(inject)。不是拦截。为此,可能需要使用 InterceptionFactory
。
不过,您的问题不需要任何一个。
CDI supports programmatic creation of beans...
您用代码( Instance<T>
)描述的不是创建,而是 dynamic/programmatic lookup 。它遵循与@Inject
相同的解析规则。只允许你让它变得动态而不是一成不变的。
如果你谈到创造,你可能指的是producer methods ?
现在,解决你的问题...
如果我理解正确,唯一的问题是 Encryptor
的构造函数有参数。好吧,那么您需要确保可以通过某种方式注入(inject)这些参数。因为它们都是 String
类型的三个。 ,您需要将它们包装在某个 bean 中或使用限定符,以便 typesafe resolution当您有多个 String
类型的 bean 时,不会因分辨率不明确而崩溃。 .
以下是基于限定符的解决方案的构造函数的外观。 @Output
, @Input
和@Key
都是qualifiers :
@Inject
public Encryptor (@Input String inputFile, @Output String outputFile, @Key String key){...}
以下是这些限定符之一的示例:
@Qualifier
@Retention(RUNTIME)
@Target({METHOD, FIELD, PARAMETER, TYPE})
public @interface Key {}
最后,您需要生成这些 String
beans,您可以使用上面提到的生产者方法来完成。这是一个(获取该值的逻辑除外,因为我不知道你是如何做到这一点的):
@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;
}
关于java - 如何强制 CDI/Weld 使用 new 关键字?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50391749/