当前位置: 首页 > 知识库问答 >
问题:

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

鄢修德
2023-03-14

我有一个命令行JavaSE应用程序,我想对它进行一点现代化。我想在其他CDI特性中使用拦截器和依赖注入。然而,应用程序的设计并没有考虑CDI或依赖项注入,它广泛使用新的关键字和构造函数参数,而不是将对象创建委托给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关键字显式创建的,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/焊接是否有类似的解决方案?还是我应该使用Spring?它是否支持构造函数和方法拦截器?

共有2个答案

蔺弘
2023-03-14

如果需要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
    }
} 

此代码的主要内容包括:

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

它应该适用于这种设计

邰博远
2023-03-14

让我们从几句话开始。。。

它根本不能处理构造函数参数

错误的这叫做构造函数注入。唯一的限制是所有参数都必须是可解析的CDIBeans。

@Inject
public Foo(Bar bar) { // -> CDI will attempt to inject Bar
 // constructor logic
}

CDI/Weld不会在使用新创建的对象上注入依赖项或运行拦截器

是的,不是默认的。但它可以通过BeanManager实现。createInjectionTarget(…)。注入(…) 不是转换现有应用程序的必经之路!

注意:以上代码将仅启用注入。不是拦截。为此,可能需要使用InterceptionFactory。但是,你不应该为了你的问题而需要任何一个。

CDI支持编程创建bean。。。

您用代码(实例)描述了什么

现在,关于你的问题。。。如果我理解正确,唯一的问题是Encryptor的构造函数有参数。那么,你需要确保这些参数可以以某种方式注入。因为它们都是String类型,所以您需要将它们包装在一些bean中,或者使用限定符,这样当您有多个String类型的bean时,类型安全解析就不会出现不明确的解析

下面是使用基于限定符的解决方案时构造函数的外观@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 {}

最后,您需要生成这些Stringbean,您可以使用上面提到的生产者方法。这里有一个(例外的逻辑,抓住这个值,因为我不知道你是如何做到的):

@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;
}

 类似资料:
  • 我在这里的部分问题是使用正确的词汇,所以我提前为可能是一个简单的术语问题道歉。 假设我有一个接口和一个实现该接口的类。 进一步假设我在某个地方有一个生产者方法(注释为),它返回一个。在内部,它返回一个新的,但这既不是这里也不是那里。 最后,假设我有另一个CDIBean,其注入点定义如下: 假设我有所有的文件就位等,并具有自举焊接或其他符合CDI-1.0的环境,因此我将得到一个不明确的定义错误。这很

  • 我想使用构造函数注入,因为它使我的单元测试更安全,更容易编写:我不能忘记设置要注入的字段。CDI支持这一点,注释的javadoc表示:“对于没有其他构造函数的公共无参数构造函数,是可选的。这使注入器能够调用默认构造函数。” 我知道其他标准需要一个无参数的构造函数(例如JAX-RS;参见这个问题),这是一个令人沮丧的问题。但即使对于像这样的简单类:

  • 我想使用Mojarra和Weld在Apache TomEE上运行一个WebApp。 是否因为TomEE已经使用了另一个CDI-Framework(OpenWebB)而出现了问题?有可能让TomEE和Weld一起运行吗?

  • 问题内容: 关闭。 此问题不符合堆栈溢出准则。它当前不接受答案。 想改善这个问题吗? 更新问题,使其成为Stack Overflow 的主题。 2年前关闭。 改善这个问题 是否有一个静态分析工具可以在IDE外部运行一致地强制使用@Override注释?CheckStyle具有MissingOverride检查,但仅适用于使用@inheritDoc Javadoc标记的方法。我正在寻找一种可以在连续

  • 尝试使用CDI 2.0建立一个maven Web项目,该项目应该在Tomcat 8.5上运行。所以我必须安装杰布斯 WELD 3。 Weld、CDI和Java EE版本之间有什么关系? 随着POM声明离开这里(只有“焊缝-servlet-核心”)… WELD - POM宣言(docs.jboss.org) 我在web上声明的“WeldTerminalListener”出现错误。这样的xml: 错误

  • 问题内容: 我在程序开始时,根据数据库中的某些内容,以编程方式在JScrollPane中添加了许多组件(JPanels,JLabels等)。 似乎对于GUI(?)而言,此过程太快了,因此JScrollPane并不总是正确更新,即,即使内部JPanel大于可见区域,滚动条也不可见。 调整窗口大小(JFrame)可以解决此问题,因为我认为Java在调整组件大小时会重新打印它们。 作为测试,我添加了一个