I'd like to register开发者_如何学Python/add a Managed Bean class programmatically (from within a Servlet init()) into application scope. How can I do that with JSF 1.2?
It is unlikely that can do do this in a programmatic manner from your application for managed beans of all scopes. BalusC has already pointed out how to do this for application scoped managed beans.
Having taken a look at how managed beans are registered in Mojarra 2.1 (a JSF 2.1 implementation); there isn't a lot of lot of elegant options available for programmatic registration of session and request scoped beans. Simply put, you either have to invoke the implementation specific classes, or you will have to create and destroy i.e. manage the beans yourself instead of relying on the JSF implementation to do this.
Populating the request and session scopes with the beans (the unmanaged way)
Note - This is referred to as the "unmanaged way" because you are constructing the beans, and not the container. Annotations like @PostConstruct
and @PreDestroy
will not work, unless you process them yourself and invoke the appropriate methods. Even dependency-injection won't work.
EL expressions are always evaluated at runtime, so it gives you enough opportunity to populate the scope with the beans before evaluation (which allows for shooting yourself in the foot, if you have the chance to do so). In Mojarra (and possibly other JSF implementations), the EL resolver will rely on the services of a ScopeHandler (or an equivalent class) to resolve the EL expression values. Mojarra uses the classes ApplicationScopeHandler
, RequestScopeHandler
and SessionScopeHandler
to obtain the values from the different scopes.
You can populate the contents of the Session and Request scopes after after a new session is created, or before a request is processed by the JSF implementation.
Session scope population can be done (ideally using a HttpSessionListener
) using:
HttpSession session = request.getSession(false);
session == null ? null : session.setAttribute("<keyname>", new Bean());
The keyname
must match the values you are using to reference the bean in EL expressions.
In a similar manner, you can populate the request scope (ideally done in a filter) using:
ServletRequest request = ... // get the reference to the servlet request object
request.setAttribute("<keyname>", new Bean());
If you need to understand how this works, you should take a look at the classes com.sun.faces.context.SessionMap
, com.sun.faces.context.RequestMap
and com.sun.faces.context.ApplicationMap
to see how the context maps are managed internally, and used by the SessionScopeHandler
, RequestScopeHandler
and ApplicationScopeHandler
classes that are static inner classes of the ScopeManager
(another static inner) class of the com.sun.faces.mgbean.BeanManager
class. The BeanManager
class is the one that contains the managed bean registrations, and the next section discusses how to "hack into" the registration process of Mojarra.
Using the Mojarra classes to register the beans
Registration of managed beans in the Mojarra implementation is done by the public void register(ManagedBeanInfo beanInfo)
method of the com.sun.faces.mgbean.BeanManager
class. It is not trivial to access the BeanManager
class using the JSF or Servlet APIs alone. There is however the ApplicationAssociate
class of Mojarra that creates the BeanManager
instance, and can be accessed using the getCurrentInstance()
method. The other answer by Thomas already demonstartes how to register the managed bean programmatically:
ApplicationAssociate.getCurrentInstance().getBeanManager().register(...)
There is a caveat with the above approach. It is unlikely that this approach will work in the init method of Servlet
for the simple reason that the getCurrentInstance
method relies on a ThreadLocal variable to retrieve the ApplicationAssociate
instance. The thread local variable is initialized by the com.sun.faces.application.WebappLifecycleListener
class, so you must reproduce the mechanism used by the WebappLifecycleListener
class, of invoking the ApplicationAssociate getInstance(ServletContext context)
method, to gain access to the ApplicationAssociate
instance. The following code therefore, might be (as I have not attempted using it) a better one, if you are willing to use Mojarra specific classes:
ServletContext sc = ... //get the ServletContext reference;
ApplicationAssociate.getInstance(sc).getBeanManager().register(...)
You must still watch out for quirks arising out of this mechanism as it is quite possible that some of the Mojarra classes and instances would not have been loaded or initialized before your Servlet. I would therefore suggest loading attempting to configure your servlet with a load-on-startup
value that is higher than the one used by the FacesServlet
.
from within a Servlet
init()
So, it concerns a non-JSF request. The FacesContext#getCurrentInstance()
would return null
here, so it's of no use for you here.
It's good to know that JSF application scoped managed beans are basically stored as an attribute of the ServletContext
. In the init()
method you've the ServletContext
at your hands by the inherited getServletContext()
method. So, the following should do:
@Override
public void init() {
getServletContext().setAttribute("managedBeanName", new BackingBean());
}
That's it. It'll be available in JSF by #{managedBeanName}
.
Try FacesContext.currentInstance().getExternalContext().getApplicationMap().put(name, bean);
, i.e. put the managed bean instance into the map using the name you want to use in your expressions.
Edit:
To register the bean, try calling: ApplicationAssociate.getCurrentInstance().getBeanManager().register(...)
and pass a ManagedBeanInfo
you filled.
The following code registers the managed bean correcly using FacesContext
, but it requires the servlet request and response. You could use the code and initialize it lazily using servlet and not during the init.
Usage:
UserBean ub = (UserBean)
Example.getBean(servletRequest, servletResponse, "user", UserBean.class);
Source:
import javax.el.ExpressionFactory;
import javax.el.ValueExpression;
import javax.faces.FactoryFinder;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import javax.faces.context.FacesContextFactory;
import javax.faces.lifecycle.LifecycleFactory;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
class Example {
public static Object getBean(HttpServletRequest request, HttpServletResponse response, String beanName, Class expectedType){
FacesContext ctx = getFacesContext(request, response);
ValueExpression vex = ctx.getApplication().getExpressionFactory().createValueExpression(ctx.getELContext(), "#{"+beanName+"}", expectedType);
return vex.getValue(ctx.getELContext());
}
private static FacesContext getFacesContext(HttpServletRequest request, HttpServletResponse response) {
FacesContext facesContext = FacesContext.getCurrentInstance();
if (facesContext == null) {
facesContext = ((FacesContextFactory) FactoryFinder.getFactory(FactoryFinder.FACES_CONTEXT_FACTORY)).
getFacesContext(request.getSession().getServletContext(), request, response,
((LifecycleFactory) FactoryFinder.getFactory(FactoryFinder.LIFECYCLE_FACTORY))
.getLifecycle(LifecycleFactory.DEFAULT_LIFECYCLE));
InnerFacesContext.setFacesContextAsCurrentInstance(facesContext);
facesContext.setViewRoot(facesContext.getApplication().getViewHandler().createView(facesContext, ""));
}
return facesContext;
}
private abstract static class InnerFacesContext extends FacesContext {
protected static void setFacesContextAsCurrentInstance(FacesContext facesContext) {
FacesContext.setCurrentInstance(facesContext);
}
}
}
Say I have registered my beans using
ApplicationAssociate.getInstance(sc).getBeanManager().register(...)
Now It is working fine, But then there is a server restart and my beans are destroyed, How on startup I can get the same bean registered.
精彩评论