java 使用 CDI @Inject 注入 Spring bean

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4144039/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 04:59:40  来源:igfitidea点击:

Injecting a Spring bean using CDI @Inject

javaspringjsfcdijboss-weld

提问by perdian

I'm trying to inject a bean defined in a Spring context into a CDI managed component but I'm not successful. The bean is not injected, instead a new instance gets created each time the injection should be performed. My environment is Tomcat 7 with JBoss Weld.

我正在尝试将 Spring 上下文中定义的 bean 注入 CDI 托管组件,但没有成功。bean 没有被注入,而是在每次应该执行注入时创建一个新实例。我的环境是带有 JBoss Weld 的 Tomcat 7。

The Spring ApplicationContext is straighforward:

Spring ApplicationContext 很简单:

<beans>
  ...
  <bean id="testFromSpring" class="test.Test" />
  ...
</bean>

The CDI managed bean looks like this:

CDI 托管 bean 如下所示:

@javax.inject.Named("testA")
public class TestA {

  @javax.inject.Inject
  private Test myTest = null;

  ...

  public Test getTest() {
    return this.myTest;
  }

}

This is my faces-config.xml

这是我的 faces-config.xml

<faces-config xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd" version="2.0">
  <application>
    <el-resolver>org.springframework.web.jsf.el.SpringBeanFacesELResolver</el-resolver>
  </application>
</faces-config>

However, when I access the testproperty from within a JSF page, a new Testinstance is being created each time the access occurs. This is a simple example:

但是,当我test从 JSF 页面中访问该属性时,Test每次发生访问时都会创建一个新实例。这是一个简单的例子:

<html>
  ...
  <p>1: <h:outputText value="#{testFromSpring}" /></p>
  <p>2: <h:outputText value="#{testA.test}" /></p>
  ...

I get the following output:

我得到以下输出:

1: test.Test@44d79c75
2: test.Test@53f336eb

After a refresh:

刷新后:

1: test.Test@44d79c75
2: test.Test@89f2ac63

I can see that the first output is correct. No matter how often I refresh the page, the testFromSpringreturns the value from the bean defined in the Spring context. However the second output clearly shows that each time the getTestmethod on the testcomponents is invoked, a new Testinstance is created and injected instead of using the instance from the Spring context as I would expect.

我可以看到第一个输出是正确的。无论我多久刷新一次页面,都会testFromSpring返回 Spring 上下文中定义的 bean 的值。然而,第二个输出清楚地表明,每次调用组件getTest上的方法时testTest都会创建并注入一个新实例,而不是像我预期的那样使用 Spring 上下文中的实例。

So, what's the reason for this behaviour?

那么,这种行为的原因是什么?

How can I inject the bean from the Spring context into the CDI managed bean?

如何将 Spring 上下文中的 bean 注入 CDI 托管 bean?

I also tried using a qualifier using the name defined in the Spring context, but now an exception is thrown indicating, that the bean cannot be found:

我还尝试使用 Spring 上下文中定义的名称使用限定符,但现在抛出异常,指示无法找到该 bean:

org.jboss.weld.exceptions.DeploymentException: WELD-001408 Injection point has unsatisfied dependencies.  Injection point:  field test.TestA.myTest;  Qualifiers:  [@javax.inject.Named(value=testFromSpring)]

for the code

对于代码

@javax.inject.Named("testA")
public class TestA {

  @javax.inject.Inject
  @javax.inject.Named("testFromSpring")
  private Test myTest = null;

回答by Bozho

Pascal is right that you can't inject something managed by spring into a weld bean (or vice-versa).

帕斯卡是对的,你不能将弹簧管理的东西注入焊接豆(反之亦然)。

But you can define a producer that gets spring beans and gives them to Weld. This sounds like an extreme hack, btw, and I don't think you are supposed to use both frameworks in one project. Choose one and remove the other. Otherwise you'll get in multiple problems.

但是您可以定义一个生产者来获取 spring bean 并将它们提供给 Weld。这听起来像是一个极端的黑客,顺便说一句,我认为您不应该在一个项目中同时使用这两个框架。选择一个并删除另一个。否则你会遇到很多问题。

Here's how it would look like.

这是它的样子。

@Qualifier
@Retention(Runtime)
public @interface SpringBean {
     @NonBinding String name();
}


public class SpringBeanProducer {

    @Produces @SpringBean
    public Object create(InjectionPoint ip) {
         // get the name() from the annotation on the injection point
         String springBeanName = ip.getAnnotations()....

         //get the ServletContext from the FacesContext
         ServletContext ctx = FacesContext.getCurrentInstance()... 

         return WebApplicationContextUtils
              .getRequiredWebApplication(ctx).getBean(springBeanName);
    }
}

Then you can have:

然后你可以有:

@Inject @SpringBean("fooBean")
private Foo yourObject;

P.S. You can make the above more type-safe. Instead of getting the bean by name, you can get, through reflection, the generic type of the injection point, and look it up in the spring context.

PS您可以使上述更类型安全。您可以通过反射获取注入点的泛型类型,而不是通过名称获取 bean,并在 spring 上下文中查找它。

回答by Pascal Thivent

I don't think Weld can inject something that is not managed (instantiated) by Weld (a Spring bean in your case).

我不认为 Weld 可以注入不是由 Weld 管理(实例化)的东西(在您的情况下是 Spring bean)。

回答by GreyFairer

There's also the JBoss Snowdrop project. I don't know if it'll work with JBoss Weld on Tomcat, the documentation describes only on JBoss 5, 6 and 7. According to http://docs.jboss.org/snowdrop/2.0.0.Final/html/ch03.html#d0e618it will inject beans declared in jboss-spring.xml into locations marked with @Spring instead of @Inject. No experience myself though, YMMV.

还有 JBoss Snowdrop 项目。我不知道它是否适用于 Tomcat 上的 JBoss Weld,文档仅在 JBoss 5、6 和 7 上进行了描述。根据http://docs.jboss.org/snowdrop/2.0.0.Final/html/ ch03.html#d0e618它将把在 jboss-spring.xml 中声明的 bean 注入到标有 @Spring 而不是 @Inject 的位置。虽然我自己没有经验,YMMV。