52

I understand that a managed bean works like a controller, because your only task is "link" the View Layer with Model.

To use a bean as a managed bean I must declare @ManagedBeanannotation, doing that I can communicate JSF with bean directly.

If I want to inject some component (from Spring) in this managedBean I have two possibles ways:

  1. Choose the property in ManagedBean (like "BasicDAO dao") and declare @ManagedProperty(#{"basicDAO"}) above the property. Doing it, i'm injecting the bean "basicDAO" from Spring in ManagedBean.

  2. Declared @Controller in ManagedBean Class, then i'll have @ManagedBean and @Controller annotations, all together. And in property "BasicDAO dao" i must use @Autowired from Spring.

Is my understanding correct?

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
Ronaldo Lanhellas
  • 2,975
  • 5
  • 46
  • 92

4 Answers4

105

@ManagedBean vs @Controller

First of all, you should choose one framework to manage your beans. You should choose either JSF or Spring (or CDI) to manage your beans. Whilst the following works, it is fundamentally wrong:

@ManagedBean // JSF-managed.
@Controller // Spring-managed.
public class BadBean {}

You end up with two completely separate instances of the very same managed bean class, one managed by JSF and another one managed by Spring. It's not directly clear which one would actually be used in EL when you reference it as #{someBean}. If you have the SpringBeanFacesELResolver registered in faces-config.xml, then it would be the Spring-managed one, not the JSF-managed one. If you don't have that, then it would be the JSF-managed one.

Also, when you declare a JSF managed bean specific scope, such as @RequestScoped, @ViewScoped, @SessionScoped or @ApplicationScoped from javax.faces.* package, it will only be recognized and used by @ManagedBean. It won't be understood by @Controller as it expects its own @Scope annotation. This defaults to singleton (application scope) when absent.

@ManagedBean // JSF-managed.
@ViewScoped // JSF-managed scope.
@Controller // Spring-managed (without own scope, so actually becomes a singleton).
public class BadBean {}

When you reference the above bean via #{someBean}, it would return the Spring-managed application scoped bean, not the JSF-managed view scoped bean.


@ManagedProperty vs @Autowired

The JSF-specific @ManagedProperty works only in JSF-managed beans, i.e. when you're using @ManagedBean. The Spring-specific @Autowired works only in Spring-managed beans, i.e. when you're using @Controller. Below approaches are less or more equivalent and cannot be mixed:

@ManagedBean // JSF-managed.
@RequestScoped // JSF-managed scope.
public class GoodBean {

    @ManagedProperty("#{springBeanName}")
    private SpringBeanClass springBeanName; // Setter required.
}
@Component // Spring-managed.
@Scope("request") // Spring-managed scope.
public class GoodBean {

    @Autowired
    private SpringBeanClass springBeanName; // No setter required.
}

Do note that when you have the SpringBeanFacesELResolver registered in faces-config.xml as per the javadoc,

<application>
    ...
    <el-resolver>org.springframework.web.jsf.el.SpringBeanFacesELResolver</el-resolver>
</application>

and thus you can reference Spring managed beans in EL via #{springBeanName}, then you can just reference them in @ManagedProperty too, as it basically sets the evaluated result of the given EL expression. The other way round, injecting a JSF managed bean via @Autowired, is in no way supported. You can however use @Autowired in a JSF managed bean when you extend your bean from SpringBeanAutowiringSupport. This will automatically register the JSF managed bean instance in Spring autowirable context during constructor invocation, which means that everything @Autowired will be available in @PostConstruct and later.

@ManagedBean // JSF-managed.
@ViewScoped // JSF-managed scope.
public class GoodBean extends SpringBeanAutowiringSupport implements Serializable {

    @Autowired
    private SpringBeanClass springBeanName; // No setter required.

    @PostConstruct
    private void init() {
        // springBeanName is now available.
    }
}

Or when your architecture doesn't allow extending beans from a different base class, then you can always manually register the JSF managed bean instance in Spring autowirable context as below. See also How to integrate JSF 2 and Spring 3 (or Spring 4) nicely for the trick.

@ManagedBean // JSF-managed.
@ViewScoped // JSF-managed scope.
public class GoodBean implements Serializable {

    @Autowired
    private SpringBeanClass springBeanName; // No setter required.

    @PostConstruct
    private void init() {
        FacesContextUtils
            .getRequiredWebApplicationContext(FacesContext.getCurrentInstance())
            .getAutowireCapableBeanFactory().autowireBean(this);

        // springBeanName is now available.
    }
}

@XxxScoped vs @Scope

Spring's @Scope has limited support for JSF scopes. There's no equivalent for JSF's @ViewScoped. You'd basically either homegrow your own scopes, or stick to manually registering the JSF managed bean instance in Spring autowirable context as shown above.

And, from the other side on, Spring WebFlow was taken over in JSF 2.2 via new @FlowScoped annotation. So if you happen to be on JSF 2.2 already, then you don't necessarily need to use Spring WebFlow if you solely want the flow scope.


CDI - trying to unify it all

Since Java EE 6, CDI is offered as standard alternative to Spring DI. It has respectively @Named and @Inject annotations for this and also its own set of scopes. I'm not sure how it interacts with Spring as I don't use Spring, but @Inject works inside a @ManagedBean, and @ManagedProperty inside a @ManagedBean can reference a @Named bean. On the other hand, @ManagedProperty doesn't work inside a @Named bean.

The purpose of CDI is to unify all different bean management frameworks into only one specification/inteface. Spring could have been a full CDI implementation, but they choosed to only partially implement it (only JSR-330 javax.inject.* is supported, but JSR-299 javax.enterprise.context.* not). See also Will Spring support CDI? and this tutorial.

JSF will be moving to CDI for bean management and deprecate @ManagedBean and friends in a future version.

@Named // CDI-managed.
@ViewScoped // CDI-managed scope.
public class BetterBean implements Serializable {

    @Inject
    private SpringBeanClass springBeanName; // No setter required.

    @PostConstruct
    private void init() {
        // springBeanName is now available.
    }
}

See also:

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • You said: "JSF-specific @ ManagedProperty works only in JSF-managed beans, i.e. when you're using @ ManagedBean". But i'm using @ ManagedProperty to refence a Spring-Bean and this is works fine. How i said: I'm using @ ManagedProperty(#{'basicDAO'}) and bean 'basicDAO' is a @ Repository bean. – Ronaldo Lanhellas Aug 22 '13 at 19:07
  • @ ManagedProperty is the approach(which i've come across) to integrate spring with jsf(inject bean). what I believe(correct me if I'm wrong) org.springframework.web.jsf.el.SpringBeanFacesELResolver propagates jsf bean to Spring Bean. And Generally we create Spring container(org.springframework.web.context.ContextLoaderListener) which constitutes jsf. And as @BalusC said as of now Spring doesn't support Java EE 6 CDI. I dearly wanted @ ConversationalScope which is not supported by spring container having JSF. – Karthik Prasad Aug 22 '13 at 19:26
  • So, ManagedProperty is like @Autowired, but in JSF. :D – Ronaldo Lanhellas Aug 22 '13 at 20:07
  • A 54 upvoted answer replaced as the accepted one by a new one that refers to deprecated annotations... Wow... – Kukeltje Dec 01 '17 at 00:48
  • @BalusC on the MVC concept, you see the JSF Managed bean as a View or a Controller? – John John Pichler Oct 08 '19 at 13:30
  • @BalusC: In the first paragraph in **CDI - trying to unify it all** the _but `@Inject` works inside a `@ManagedBean`_ is most likely a typo? Or I've learned something new. – Kukeltje Jun 15 '20 at 19:52
  • @Kukeltje: Not a typo. The `@Inject` can be used inside a `@ManagedBean` to inject a CDI bean or an EJB. In hindsight, it might have been Mojarra specific, not JSF API specific. – BalusC Jun 16 '20 at 09:36
14

There is another way to use Spring-managed beans in JSF-managed beans by simply extending your JSF bean from SpringBeanAutowiringSupport and Spring will handle the dependency injection.

@ManagedBean // JSF-managed.
@ViewScoped // JSF-managed scope.
public class GoodBean extends SpringBeanAutowiringSupport {

    @Autowired
    private SpringBeanClass springBeanName; // No setter required.

    // springBeanName is now available.
}
Selcuk
  • 857
  • 2
  • 11
  • 32
  • I know `@ManagedBean` is ***DEPRECATED*** in JSF. But as you see the questioner mentions `@ManagedBean` in the question and again as you see "54 upvoted answer" mentions about `@ManagedBean` as well. And again as you see, I started my answer by saying "There is another way". Honestly, I didn't thought that my answer would be the accepted one instead of @BalusC's excellent answer. I just wanted to show another easier way. I don't understand how could I deserve those "I'm almost inclined to downvote" words :) "same person that asked the question?" lol, no :D Have you ever tried that before? :) – Selcuk Dec 02 '17 at 00:26
1

The easy way to do this is via XML. I used @Component in already made jsf managed bean but @Autowired did not work because managed bean was already there in faces-config.xml. If it is mandatory to keep that managed bean definition along with its managed property in the xml file then it is suggested to add the spring bean as another managed property inside the managed bean tag. Here the spring bean is there defined in spring-config.xml(can be autowired somewhere alternately). please refer https://stackoverflow.com/a/19904591/5620851

edited by me. I suggest to either implement it altogether through annotation @Managed and @Component or via xml for both.

Community
  • 1
  • 1
hi.nitish
  • 2,732
  • 2
  • 14
  • 21
0

You can autowire individual beans without @Autowired by leveraging getBean of the current WebApplication context.

Please refer to @BalusC's answer for more details. This is just a slight modification over his example:

@ManagedBean // JSF-managed.
@ViewScoped // JSF-managed scope.
public class GoodBean implements Serializable {

    // @Autowired // No Autowired required
    private SpringBeanClass springBeanName; // No setter required.

    @PostConstruct
    private void init() {
        WebApplicationContext ctx = FacesContextUtils.getWebApplicationContext(FacesContext.getCurrentInstance());
        this.springBeanName = ctx.getBean(SpringBeanClass.class);
        // springBeanName is now available.
    }
}
Mahmoud
  • 9,729
  • 1
  • 36
  • 47