23

是否可以使用 CDI 将参数注入方法调用?预期的行为将类似于场注入。查找首选生产商并使用产品。

我想做的是:

public void foo(@Inject Bar bar){
  //do stuff
} 

或者这个(使用较少混淆的语法):

public void foo(){
  @Inject 
  Bar bar;
  //do stuff
} 

这种语法在这两种情况下都是非法的。有替代方案吗?如果不是 - 如果可能的话,出于某种原因,这会是一个坏主意吗?

谢谢

编辑——我的要求可能不够清楚——我希望能够直接调用该方法,将bar变量的初始化留给容器。Jörn Horstmann 和 Perception 的回答表明这是不可能的。

4

6 回答 6

21

当容器实例化 bean 时,会为 bean 处理注入点,这确实限制了方法级注入的用例数量。当前版本的规范承认以下类型的方法注入:

初始化方法注入

public class MyBean {
    private Processor processor;

    @Inject
    public void setProcessor(final Processor processor) {
        this.processor = processor;
    }
}

当一个实例MyBean被注入时,处理器实例也将被注入,通过它的setter方法。

事件观察者方法

public class MyEventHandler {
    public void processSomeEvent(@Observes final SomeEvent event) {
    }
}

事件实例被直接注入到事件处理方法中(虽然,不是使用@Inject 注解)

生产者方法

public class ProcessorFactory {
    @Produces public Processor getProcessor(@Inject final Gateway gateway) {
        // ...
    }
}

生产者方法的参数会自动注入。

于 2012-05-23T11:32:42.807 回答
10

如果您真正想要的不是方法的参数(应该由调用者提供),而是每次调用方法时正确初始化的 CDI bean 实例,并且完全构造和注入,然后检查

javax.inject.Provider<T>

基本上,首先为类注入一个提供者

@Inject Provider<YourBean> yourBeanProvider;

然后,在方法中,获取一个新的实例

YourBean bean = yourBeanProvider.get();

希望这可以帮助 :)

于 2014-04-01T04:13:51.640 回答
8

当我最初对这个主题进行搜索时出现了这个问题,从那以后我了解到随着 CDI 1.1(包含在 JavaEE 7 规范中)的发布,现在有一种方法可以部分地执行 OP 想要的操作。你还是做不到

public void foo(@Inject Bar bar){
   //do stuff
}

但是您可以“注入”一个局部变量,尽管您不使用@Inject而是以编程方式查找注入的实例,如下所示:

public void foo() {
    Instance<Bar> instance = CDI.current().select(Bar.class);
    Bar bar = instance.get();
    CDI.current().destroy(instance);
    // do stuff with bar here
}

请注意,该select()方法可选地采用您可能需要提供的任何限定符注释。祝你好运获得java.lang.annotation.Annotation虽然的实例。遍历您的Instance<Bar>内容以找到您想要的可能会更容易。

我被告知您需要Instance<Bar>像我上面所做的那样销毁它,并且可以根据经验验证上述代码是否有效;但是,我不能发誓你需要摧毁它。

于 2016-02-17T17:41:07.120 回答
5

CDI 的该功能称为“初始化方法”。语法与您的代码不同,因为整个方法都用 进行@Inject了注释,方法参数可以通过限定符进一步注释以选择特定的 bean。JSR 299的第 3.9 节显示了以下示例,它@Selected是一个限定符,如果只有一个 bean 实现,则可以省略该限定符。

@Inject
void setProduct(@Selected Product product) {
    this.product = product;
}

请注意

应用程序可以直接调用初始化方法,但容器不会向该方法传递任何参数。

于 2012-05-23T11:19:57.203 回答
1

您可以在您的方法中使用 BeanManager API 来获取上下文引用,或者根据您的最终目标,您可以注入一个

Instance<Bar>

在方法之外并在方法中使用它。

于 2012-05-27T22:08:29.387 回答
0

如果您的目标是通过反射调用该方法,则可InjectionPoint以为每个参数创建一个。

以下是使用 CDI-SE 的示例:

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.context.Dependent;
import javax.enterprise.context.spi.CreationalContext;
import javax.enterprise.inject.se.SeContainer;
import javax.enterprise.inject.se.SeContainerInitializer;
import javax.enterprise.inject.spi.AnnotatedMethod;
import javax.enterprise.inject.spi.AnnotatedType;
import javax.enterprise.inject.spi.BeanManager;

public class ParameterInjectionExample {

    public static class Foo {

        // this method will be called by reflection, all parameters will be resolved from the BeanManager
        // calling this method will require 2 different Bar instances (which will be destroyed at the end of the invocation)
        public void doSomething(Bar bar, Baz baz, Bar bar2) {
            System.out.println("got " + bar);
            System.out.println("got " + baz);
            System.out.println("got " + bar2);
        }
    }

    @Dependent
    public static class Bar {

        @PostConstruct
        public void postConstruct() {
            System.out.println("created " + this);
        }

        @PreDestroy
        public void preDestroy() {
            System.out.println("destroyed " + this);
        }
    }

    @ApplicationScoped
    public static class Baz {

        @PostConstruct
        public void postConstruct() {
            System.out.println("created " + this);
        }

        @PreDestroy
        public void preDestroy() {
            System.out.println("destroyed " + this);
        }
    }

    public static Object call(Object target, String methodName, BeanManager beanManager) throws Exception {
        AnnotatedType<?> annotatedType = beanManager.createAnnotatedType(target.getClass());
        AnnotatedMethod<?> annotatedMethod = annotatedType.getMethods().stream()
                .filter(m -> m.getJavaMember().getName().equals(methodName))
                .findFirst() // we assume their is only one method with that name (no overloading)
                .orElseThrow(NoSuchMethodException::new);
        // this creationalContext will be valid for the duration of the method call (to prevent memory leaks for @Dependent beans)
        CreationalContext<?> creationalContext = beanManager.createCreationalContext(null);
        try {
            Object[] args = annotatedMethod.getParameters().stream()
                    .map(beanManager::createInjectionPoint)
                    .map(ip -> beanManager.getInjectableReference(ip, creationalContext))
                    .toArray();
            return annotatedMethod.getJavaMember().invoke(target, args);
        } finally {
            creationalContext.release();
        }
    }

    public static void main(String[] args) throws Exception {
        try (SeContainer container = SeContainerInitializer.newInstance().disableDiscovery().addBeanClasses(Bar.class, Baz.class).initialize()) {
            System.out.println("beanManager initialized");
            call(new Foo(), "doSomething", container.getBeanManager());
            System.out.println("closing beanManager");
        }
    }
}
于 2021-05-08T13:56:41.353 回答