Spring JSF integration: how to inject a Spring component/service in JSF managed bean?
@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:
- When is it necessary or convenient to use Spring or EJB3 or all of them together?
- JSF Service Layer
- Backing beans (@ManagedBean) or CDI Beans (@Named)?
- Using JSF as view technology of Spring MVC
- How to install and use CDI on Tomcat?
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.
}