In the JSF application, a Servlet is configured in web. xml, called javax. faces. webapp. FacesServlet. You can see that the process of viewing and understanding a request can start from here. Download the source code of JSF from the official website. The project name is odd and is called "mojarra". I see the version 1.2 _ 12_b01. It contains two sub-projects. One is jsf-api, which contains mostly interfaces and a few key classes. Another project is called jsf-ri. After a long time, I finally figured out the meaning of reference implementation with my brother's reminder. Jsf-api is part of the JavaEE standard, and the type package names in it are all javax. faces, while the jsf-ri project is a reference implementation by sun for the JSF standard. All types of packages in the project are com. sun. start with faces.
FacesServlet initialize FacesServlet # init)
During JSF request processing, the system will initialize FacesServlet and call the init method. There are two main tasks, one is to initialize FacesContextFactory, and the other is to initialize the Lifecycle object. In the jsf-api project, the FacesServlet class is the implementation class of a Servlet interface, while FacesContextFactory and Lifecycle are both interfaces. The jsf-ri project has the implementation classes of these two interfaces, namely com. sun. faces. context. FacesContextFactoryImpl and com. sun. faces. lifecycle. LifecycleImpl. Take it for granted that FacesServlet should determine the specific FacesContextFactory and Lifecycle implementation classes based on some configurations during initialization, that is, here, the "JSF standard" is in line with the "JSF implementation. Other JSF implementations such as MyFaces should be similar to two methods. One is to change the configuration value required in the init method of FacesServlet, so we can enable our own FacesContextFactory implementation and Lifecycle implementation, the subsequent processing process follows its own logic. The second method is a bit dumb and unlikely. It is to replace the FacesServlet overwrite and there is no need to read any configuration, you can simply use your own implementation class. However, this practice is probably not in line with the JSF specification, and it is only possible for me to do so. The main code is as follows:
- 1 facesContextFactory = (FacesContextFactory)FactoryFinder.
getFactory(FactoryFinder.FACES_CONTEXT_FACTORY);
- 2
- 3 LifecycleFactory lifecycleFactory = (LifecycleFactory)FactoryFinder.
getFactory(FactoryFinder.LIFECYCLE_FACTORY);
Let's look back at the initialization result. FacesContextFactory is obviously used to produce such a thing as FacesContext. FacesContext can be seen as a RequestWrapper. Note that FaceContext is different from ServletContext. ServletContext is a global object with only one Web application. It corresponds to a Web application, A FacesContext corresponds to a request. In addition, the RequestWrapper statement is not strict. In fact, the FacesContext contains ServletContext and Response ). LifeCycle can be seen as a Filter Chain similar to the Filter Chain in servlet specifications ). Therefore, the whole JSF request processing process is actually a user request encapsulated into FaceContext, through the LifeCycle process similar to a Filter Chain.
This overview clearly shows the service method of FacesServlet. During the initialization of FacesServlet, a global FacesContextFactory object and LifeCycle object are constructed. FacesContextFactory can be regarded as a "request packaging factory", so it is obvious that when a request arrives at FacesServlet, the first step is to take the request and package it in the packaging factory, the packaging result is a FacesContext. The Code is as follows:
FacesContext context = facesContextFactory. getFacesContext (servletConfig. getServletContext (), request, response, lifecycle );
In the packaging process, a com. sun. faces. context. FacesContextImpl object is created. The FacesContextImpl class inherits javax. faces. context. FacesContext in the jsf-api project. The first parameter of the FacesContextImpl constructor is the implementation of an interface called ExternalContext. Check its source code. We can see that the ExternalContextImpl class is coupled with the Servlet API, and FacesContextImpl is irrelevant to the Servlet API. In fact, here, JSF can be used not only in the Servlet environment, as described in the comments of the ExternalContext interface, the difference between using JSF In the Servlet environment and using JSF In the Portlet environment is achieved, actually, different ExternalContext is used. The code for constructing FacesContextImpl in FacesContextFactoryImpl is as follows:
- FacesContext ctx = new FacesContextImpl
- (new ExternalContextImpl((ServletContext) sc,
- (ServletRequest) request,(ServletResponse) response),
- lifecycle);
In the contextimpl Construction Method of FacesContextImpl, another thing is to determine the RenderKitFactory according to the configuration. Obviously, different RenderKitFactory can generate different RenderKit, different RenderKit objects are targeted at different clients. Therefore, different RenderKit may be used for browsers, mobile devices, and so on. The code for constructing FacesContextImpl is as follows:
- this.externalContext = ec;
- setCurrentInstance(this);
- this.rkFactory = (RenderKitFactory)FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY);
FacesContext is often used in the code. getCurrentInstance () is a static method to obtain the FacesContext object corresponding to the current request. In fact, there is a static ThreadLocal object in the FacesContext class to store the FacesContext object corresponding to the current request thread, in the above Code, setCurrentInstance (this) is to put the currently constructed FacesContext object into ThreadLocal.
After FacesContext is created, as mentioned above, it should be processed step by step through the "Filter Chain" of LifeCycle. So what is a Filter in the Chain of LifeCycle? The answer is Phases.
FacesServlet divides FaceContext into two parts through LifeCycle processing. One part is to call the execute method of LifeCycle and execute the logic. The second part is to call the render method of LifeCycle to display the response. The code in FacesServlet. service is as follows:
- lifecycle.execute(context);
- lifecycle.render(context);
In the implementation of LifeCycleImpl, an array of Phase objects is stored and seven Phase objects are stored. The first one is null, followed by view reconstruction, application request value, verification, model value update, application execution, and response rendering. In the execute method, five Phase files from view reconstruction to application execution are called, and in the render method, the last Phase is called to present the response. In the LifeCycleImpl class, the Code is as follows:
- //The Phase instance for the render() method
- private Phase response = new RenderResponsePhase();
-
- // The set of Phase instances that are executed by the execute() method
- // in order by the ordinal property of each phase
- private Phase[] phases = {
- null, // ANY_PHASE placeholder, not a real Phase
- new RestoreViewPhase(),
- new ApplyRequestValuesPhase(),
- new ProcessValidationsPhase(),
- new UpdateModelValuesPhase(),
- new InvokeApplicationPhase(),
- response
- };
In Servlet Filter, each Filter determines whether to call the next Filter to determine whether to allow the request to continue to pass the subsequent Filter in Filter Chains. This is a chain call process. In the execute method of LifeCycle, several Phase statements are executed in the order of a for loop. After each Phase is executed, the FaceContext object will check whether the mark renderResponse is set in the FaceContext object to stop subsequent processing and directly present the response) or if the response has been completed, no subsequent processing is required or the response is completed in the rendering response Phase. If the flag is true, the subsequent Phase will not be executed.
The main code of the LifeCycleImpl execute method is as follows:
- for (int i = 1, len = phases.length -1 ; i < len; i++) { // Skip ANY_PHASE placeholder
-
- if (context.getRenderResponse() ||
- context.getResponseComplete()) {
- break;
- }
-
- phases[i].doPhase(context, this, listeners.listIterator());
-
- }
In the render method of LifeCycle, The responseComplete status of FacesContext is also checked. If it is true, render Phase is no longer executed. So now we know how to call the responseComplete method of FacesContext and the working principle of renderResponse in some of our own code or some code in the JSF library. The main code of the render method is as follows:
- if (!context.getResponseComplete()) {
- response.doPhase(context, this,listeners.listIterator());
- }
Note that the Phase concept, interface, and several implementations are all in the jsf-ri project, but the Phase concept does not exist in the jsf-api. Therefore, LifeCycle is the content of the JSF standard, and the implementation of request processing through several Phase is sun's reference implementation.
Finally, during the JSF request processing process, we can see that the doPhase method is called for each phase, and LifeCycle and FacesContext are passed in as parameters. It is worth noting that the so-called phaseListener is also introduced into the doPhase method of phase, so we can understand the principle of this "phase listener.
- Use Acegi to protect JSF applications
- JSF technology and components
- JSF Development Problems and Solutions
- Analysis of unit tests for JSF Projects
- View technology designed for JSF by Facelets