繁体   English   中英

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

[英]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
    }
} 

关于此代码的主要内容是:

  • 使用范围注释ApplicationScopedDependent
  • 在您的Encryptor类上提供init()方法,该方法将使用运行时参数
  • init()的末尾返回Encryptor的实例,以能够调用run()方法,该方法受保护以避免直接调用(我认为也可以是private ),而无需先调用init()

它应与此设计一起使用。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM