java - 如何强制 CDI/Weld 使用 new 关键字?

标签 java spring dependency-injection cdi weld

我有一个命令行 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/

相关文章:

java - Android:使用 iv 和 key 使用 AES 256 位加密来加密字符串

java - Spring Boot Main 和 JavaFX

Spring Boot - 如何获取正在运行的端口

php - PHP4 有依赖注入(inject)框架吗?

java - Spring Boot 组件扫描无法识别不同 bean 的限定符注释

java - .equals() 和 == 的问题

java - 如何用java画一个盒子,里面可以画各种形状?

java - 当自动化测试因 Unresolved 错误而失败时,最佳实践是什么?

java - 带有配置服务器的 Spring Cloud Eureka

android - 在 WorkManager 的 Worker 中注入(inject) Repository