Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Injecting a Spring bean using CDI @Inject

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;
like image 600
Christian Seifert Avatar asked Nov 10 '10 11:11

Christian Seifert


People also ask

How do you inject beans in the Spring?

In Spring Boot, we can use Spring Framework to define our beans and their dependency injection. The @ComponentScan annotation is used to find beans and the corresponding injected with @Autowired annotation. If you followed the Spring Boot typical layout, no need to specify any arguments for @ComponentScan annotation.

What does the @inject annotation do?

The annotation @Inject allows Dagger to call the constructor of the injected class to create an instance of a class, twitterApi . However, there are some cases that we may not call a constructor directly. For example: Interfaces.

What is @inject in Spring?

@Inject and @Autowired both annotations are used for autowiring in your application. @Inject annotation is part of Java CDI which was introduced in Java 6, whereas @Autowire annotation is part of spring framework. Both annotations fulfill same purpose therefore, anything of these we can use in our application.

What is the difference between @inject and @autowired?

The behaviour of the @Autowired annotation is similar to the @Inject annotation. The only difference is that the @Autowired annotation is part of the Spring framework. This annotation has the same execution paths as the @Inject annotation, listed in order of precedence: Match by Type.


2 Answers

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.

like image 76
Bozho Avatar answered Nov 07 '22 12:11

Bozho


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

like image 26
Pascal Thivent Avatar answered Nov 07 '22 11:11

Pascal Thivent