10

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

Spring ApplicationContext 很简单:

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

CDI 托管 bean 如下所示:

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

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

  ...

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

}

这是我的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>

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

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

我得到以下输出:

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

刷新后:

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

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

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

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

我还尝试使用使用 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)]

对于代码

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

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

3 回答 3

15

帕斯卡是对的,您不能将 spring 管理的东西注入焊接 bean(反之亦然)。

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

这是它的样子。

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

然后你可以拥有:

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

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

于 2010-11-10T12:15:41.930 回答
4

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

于 2010-11-10T11:46:45.540 回答
2

还有 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。

于 2011-11-06T23:09:07.073 回答