Injecting a Spring bean using CDI @Inject
Asked Answered
T

3

12

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.

The Spring ApplicationContext is straighforward:

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

The CDI managed bean looks like this:

@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 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 test property from within a JSF page, a new Test instance is being created each time the access occurs. This is a simple example:

<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 testFromSpring returns the value from the bean defined in the Spring context. However the second output clearly shows that each time the getTest method on the test components is invoked, a new Test instance is created and injected instead of using the instance from the Spring context as I would expect.

So, what's the reason for this behaviour?

How can I inject the bean from the Spring context into the CDI managed 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:

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;
Todo answered 10/11, 2010 at 11:36 Comment(2)
This could be lack of coffee here, but why inject a private member of your class (that is already set to null). Would it not be simpler to provide myTest as part of TestA's constructor (e.g. Constructor injection)Epistaxis
The injection is just an example and not the point of the question.Todo
A
15

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.

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.

Always answered 10/11, 2010 at 12:15 Comment(1)
You cannot produce Object and match with Foo injection point in CDIFarrell
A
4

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

Antinomy answered 10/11, 2010 at 11:46 Comment(0)
E
2

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#d0e618 it will inject beans declared in jboss-spring.xml into locations marked with @Spring instead of @Inject. No experience myself though, YMMV.

Expectorant answered 6/11, 2011 at 23:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.