JSF 源代码赏析之FacesServlet

来源:互联网 发布:数据归档 编辑:程序博客网 时间:2024/04/29 00:18

学习JSF 多日,现在开始看看源代码。
首先是FacesServlet类了,作为一个前端控制器,每一个JSF请求都要通过FacesServlet,然后再到其他阶段,那么,FacesServlet 到底做了些什么操作呢?

文件头部的注释
  1. /**
  2. *

    FacesServlet is a servlet that manages the request

  3. * processing lifecycle for web applications that are utilizing JavaServer
  4. * Faces to construct the user interface.

     

  5. */
头部注释说的很明白,管理请求的处理周期。至于怎么管理,下面先来看一看到底声明了什么变量
变量
  1. public static final String CONFIG_FILES_ATTR =
  2. "javax.faces.CONFIG_FILES";
  3. public static final String LIFECYCLE_ID_ATTR =
  4. "javax.faces.LIFECYCLE_ID";
  5. private static final Logger LOGGER =
  6. Logger.getLogger("javax.faces.webapp", "javax.faces.LogStrings");
  7. private FacesContextFactory facesContextFactory = null;
  8. private Lifecycle lifecycle = null;
  9. private ServletConfig servletConfig = null;

上面这些变量都是FacesServlet的全局变量,也就是整个JSF 应用的全局变量,其中最主要的我都加粗了,可以看出,主要涉及到FacesContextFactory、LifeCycle和ServletConfig对象,其中的ServletConfig对象不难理解,基于Servlet技术的表现层框架都需要这个类,而FacesContextFactory和LifeCycle则有些研究了。

FacesContextFactory是一个实现了工厂模式的抽象类,用来创建(如果没有的话)和返回一个FacesContext实例,并且把这个实例初始化,以便处理request和response对象。至于这个FacesContext对象,则是始终贯彻在JSF中的一个对象,下面自然会慢慢讲解,现在需要知道的是,FacesContext也是一个抽象类就可以。
现在先看一下FacesContextFactory对象和FacesContext的关系。顾名思义,工厂模式,就是专门生产产品的,FacesContextFactory工厂则是专门产生FacesContext对象的,FacesContextFactory对象提供了下面的方法:
FacesContextFactory
  1. public abstract FacesContext getFacesContext
  2. (Object context, Object request,
  3. Object response, Lifecycle lifecycle)
  4. throws FacesException;

来产生FacesContext对象,并且这是一个抽象方法,如何调用,则是JSF实现的事情了,并且FacesContextFactory会为每一个Request请求返回一个FacesContext对象。注意,这里用的是“返回”,而不是生成,是因为FacesContextFactory并不一定会为每一个请求生成一个新的FacesContext对象,FacesContext对象有一个release方法,这个方法负责释放FacesContext的资源,在调用这个方法之前,通过FacesContext.getCurrentInstance可以返回当前线程上的实例,这样实现FacesContext在某种程度上的重用和pool。
下面应该来看看在FacesServlet中如何调用FacesContextFactory来产生一个FacesContext对象了。
首先要产生一个FacesContextFactory对象,这是通过FacesServlet的init方法来实现的:
FacesServlet的init方法
  1. public void init(ServletConfig servletConfig) throws ServletException {
  2. // Save our ServletConfig instance
  3. this.servletConfig = servletConfig;
  4. // Acquire our FacesContextFactory instance
  5. try {
  6. facesContextFactory = (FacesContextFactory)
  7. FactoryFinder.getFactory
  8. (FactoryFinder.FACES_CONTEXT_FACTORY);
  9. } catch (FacesException e) {
  10. ResourceBundle rb = LOGGER.getResourceBundle();
  11. String msg = rb.getString("severe.webapp.facesservlet.init_failed");
  12. Throwable rootCause = (e.getCause() != null) ? e.getCause() : e;
  13. LOGGER.log(Level.SEVERE, msg, rootCause);
  14. throw new UnavailableException(msg);
  15. }
  16. // Acquire our Lifecycle instance
  17. try {
  18. LifecycleFactory lifecycleFactory = (LifecycleFactory)
  19. FactoryFinder.getFactory(FactoryFinder.LIFECYCLE_FACTORY);
  20. String lifecycleId ;
  21. // First look in the servlet init-param set
  22. if (null == (lifecycleId = servletConfig.getInitParameter(LIFECYCLE_ID_ATTR))) {
  23. // If not found, look in the context-param set
  24. lifecycleId = servletConfig.getServletContext().getInitParameter
  25. (LIFECYCLE_ID_ATTR);
  26. }
  27. if (lifecycleId == null) {
  28. lifecycleId = LifecycleFactory.DEFAULT_LIFECYCLE;
  29. }
  30. lifecycle = lifecycleFactory.getLifecycle(lifecycleId);
  31. } catch (FacesException e) {
  32. Throwable rootCause = e.getCause();
  33. if (rootCause == null) {
  34. throw e;
  35. } else {
  36. throw new ServletException(e.getMessage(), rootCause);
  37. }
  38. }
  39. }

在这个init方法中,FacesServlet通过FactoryFinder对象来创建一个具体的Factory对象,这样就把创建Factory对象的工作给托管给其他的类了,同时这个FactoryFinder还可以创建其他的工厂类,因此可以说FactoryFinder是“工厂的工厂”,是专门创造工厂的类。通过FactoryFinder.FACES_CONTEXT_FACTORY参数指明是创建FacesContextFactory,FactoryFinder就给创建出一个FacesContextFactory。
下面我们就来看看FactoryFinder是通过什么算法,来查找和创建JSF实现中的各个工厂类。
FactoryFinder通过实现标准的发现算法,可以查找所有在JSF API中指定的factory对象,这个算法是这样的:
1.如果
web应用的WEB-INF目录下存在JSF的configuration 文件,并且含有factory节点,而且这个factory节点中含有正在查找的factory对象的类名称,那么就加载这个类。
2.如果在ServletContext的初始化参数中有
javax.faces.CONFIG_FILES参数,并且这个参数值指定的配置文件中有factory节点,并且这个节点中含有目前正在查找的factory类名,那么就加载这个对象。
3.如果在ServletContext的资源目录下的Jar包中的 META-INF目录下含有JSF配置文件,并且正在查找的factory类名存在于factory节点中,则加载这个类。最晚加载的类优先。
4.如果META-INF/service/目录下有当前正在查找的类名称,会加载之。
5.如果上面的规则都没有匹配,则会使用JSF实现中的特定类。

这种算法的缺点就是每一个Web应用都会有一个自己的factory实例,不管这个JSF实现是包含在Web应用chengx程序之中还是在容器中作为一个共享库存在。

这个FactoryFinder还是蛮复杂的,以后有时间将另外撰文研究。
下面的事情就是LifecycleFactory的加载了,其加载过程不必多言。
LifecycleFactory对象加载后,会查找JSF中是否配置了javax.faces.LIFECYCLE_ID参数,根据这个参数加载lifecycleId,整个过程是这样的:
加载LifecycleFactory
 
  1. // Acquire our Lifecycle instance  
  2.        try {  
  3.            LifecycleFactory lifecycleFactory = (LifecycleFactory)  
  4.                FactoryFinder.getFactory(FactoryFinder.LIFECYCLE_FACTORY);  
  5.            String lifecycleId ;  
  6.   
  7.            // First look in the servlet init-param set  
  8.            if (null == (lifecycleId = servletConfig.getInitParameter(LIFECYCLE_ID_ATTR))) {  
  9.                // If not found, look in the context-param set   
  10.                lifecycleId = servletConfig.getServletContext().getInitParameter  
  11.                    (LIFECYCLE_ID_ATTR);  
  12.            }  
  13.   
  14.            if (lifecycleId == null) {  
  15.                lifecycleId = LifecycleFactory.DEFAULT_LIFECYCLE;  
  16.            }  
  17.            lifecycle = lifecycleFactory.getLifecycle(lifecycleId);  
  18.        } catch (FacesException e) {  
  19.            Throwable rootCause = e.getCause();  
  20.            if (rootCause == null) {  
  21.                throw e;  
  22.            } else {  
  23.                throw new ServletException(e.getMessage(), rootCause);  
  24.            }  
  25.        }  


通过加载不同实现的LifecycleFactory对象,就可以允许加载不同的Lifecycle对象,这对于扩展JSF的功能是非常重要的,
当没有显示表明lifecycleId时,lifecycleFactory就会加载默认的lifecycleId,并根据lifecycleId加载Lifecycle对象
    Lifecycle类负责JSF请求处理的全过程,主要是通过执行其中的execute方法和render方法实现的,FacesServlet的service方法很好的说明了这一点:

java 代码
  1. public void service(ServletRequest request,   
  2.                        ServletResponse response)   
  3.        throws IOException, ServletException {   
  4.   
  5.        // If prefix mapped, then ensure requests for /WEB-INF are   
  6.        // not processed.   
  7.        String pathInfo = ((HttpServletRequest) request).getPathInfo();   
  8.        if (pathInfo != null) {   
  9.            pathInfo = pathInfo.toUpperCase();   
  10.            if (pathInfo.startsWith("/WEB-INF/")   
  11.                || pathInfo.equals("/WEB-INF")   
  12.                || pathInfo.startsWith("/META-INF/")   
  13.                || pathInfo.equals("/META-INF")) {   
  14.                ((HttpServletResponse) response).   
  15.                      sendError(HttpServletResponse.SC_NOT_FOUND);   
  16.                return;   
  17.            }   
  18.        }       
  19.           
  20.        // Acquire the FacesContext instance for this request   
  21.        FacesContext context = facesContextFactory.getFacesContext   
  22.            (servletConfig.getServletContext(), request, response, lifecycle);   
  23.   
  24.        // Execute the request processing lifecycle for this request   
  25.        try {   
  26.            lifecycle.execute(context);   
  27.            lifecycle.render(context);   
  28.        } catch (FacesException e) {   
  29.            Throwable t = e.getCause();   
  30.            if (t == null) {   
  31.                throw new ServletException(e.getMessage(), e);   
  32.            } else {   
  33.                if (t instanceof ServletException) {   
  34.                    throw ((ServletException) t);   
  35.                } else if (t instanceof IOException) {   
  36.                    throw ((IOException) t);   
  37.                } else {   
  38.                    throw new ServletException(t.getMessage(), t);   
  39.                }   
  40.            }   
  41.        }   
  42.        finally {   
  43.            // Release the FacesContext instance for this request   
  44.            context.release();   
  45.        }   
  46.   
  47.    }   


好了,FacesServlet的源码我们就看到这里,下一篇中我们将深入研究Lifecycle对象的执行过程,在最后,就让我们用myFaces的FacesServlet实现来结束吧:

myfaces之FacesServlet:
  1. public final class FacesServlet implements Servlet {   
  2.     private static final Log log = LogFactory.getLog(FacesServlet.class);   
  3.     public static final String CONFIG_FILES_ATTR = "javax.faces.CONFIG_FILES";   
  4.     public static final String LIFECYCLE_ID_ATTR = "javax.faces.LIFECYCLE_ID";   
  5.   
  6.     private static final String SERVLET_INFO = "FacesServlet of the MyFaces API implementation";   
  7.     private ServletConfig _servletConfig;   
  8.     private FacesContextFactory _facesContextFactory;   
  9.     private Lifecycle _lifecycle;   
  10.   
  11.     public FacesServlet() {   
  12.         super();   
  13.     }   
  14.   
  15.     public void destroy() {   
  16.         _servletConfig = null;   
  17.         _facesContextFactory = null;   
  18.         _lifecycle = null;   
  19.         if (log.isTraceEnabled())   
  20.             log.trace("destroy");   
  21.     }   
  22.   
  23.     public ServletConfig getServletConfig() {   
  24.         return _servletConfig;   
  25.     }   
  26.   
  27.     public String getServletInfo() {   
  28.         return SERVLET_INFO;   
  29.     }   
  30.   
  31.     private String getLifecycleId() {   
  32.         String lifecycleId = _servletConfig.getServletContext()   
  33.                 .getInitParameter(LIFECYCLE_ID_ATTR);   
  34.         return lifecycleId != null ? lifecycleId   
  35.                 : LifecycleFactory.DEFAULT_LIFECYCLE;   
  36.     }   
  37.   
  38.     public void init(ServletConfig servletConfig) throws ServletException {   
  39.         if (log.isTraceEnabled())   
  40.             log.trace("init begin");   
  41.         _servletConfig = servletConfig;   
  42.         _facesContextFactory = (FacesContextFactory) FactoryFinder   
  43.                 .getFactory(FactoryFinder.FACES_CONTEXT_FACTORY);   
  44.         // TODO: null-check for Weblogic, that tries to initialize Servlet   
  45.         // before ContextListener   
  46.   
  47.         // Javadoc says: Lifecycle instance is shared across multiple   
  48.         // simultaneous requests, it must be implemented in a thread-safe   
  49.         // manner.   
  50.         // So we can acquire it here once:   
  51.         LifecycleFactory lifecycleFactory = (LifecycleFactory) FactoryFinder   
  52.                 .getFactory(FactoryFinder.LIFECYCLE_FACTORY);   
  53.         _lifecycle = lifecycleFactory.getLifecycle(getLifecycleId());   
  54.         if (log.isTraceEnabled())   
  55.             log.trace("init end");   
  56.     }   
  57.   
  58.     public void service(ServletRequest request, ServletResponse response)   
  59.             throws IOException, ServletException {   
  60.   
  61.         HttpServletRequest httpRequest = ((HttpServletRequest) request);   
  62.         String pathInfo = httpRequest.getPathInfo();   
  63.   
  64.         // if it is a prefix mapping ...   
  65.         if (pathInfo != null  
  66.                 && (pathInfo.startsWith("/WEB-INF") || pathInfo   
  67.                         .startsWith("/META-INF"))) {   
  68.             StringBuffer buffer = new StringBuffer();   
  69.   
  70.             buffer.append(" Someone is trying to access a secure resource : "  
  71.                     + pathInfo);   
  72.             buffer   
  73.                     .append("/n remote address is "  
  74.                             + httpRequest.getRemoteAddr());   
  75.             buffer.append("/n remote host is " + httpRequest.getRemoteHost());   
  76.             buffer.append("/n remote user is " + httpRequest.getRemoteUser());   
  77.             buffer.append("/n request URI is " + httpRequest.getRequestURI());   
  78.   
  79.             log.warn(buffer.toString());   
  80.   
  81.             // Why does RI return a 404 and not a 403, SC_FORBIDDEN ?   
  82.   
  83.             ((HttpServletResponse) response)   
  84.                     .sendError(HttpServletResponse.SC_NOT_FOUND);   
  85.             return;   
  86.         }   
  87.   
  88.         if (log.isTraceEnabled())   
  89.             log.trace("service begin");   
  90.         FacesContext facesContext = _facesContextFactory.getFacesContext(   
  91.                 _servletConfig.getServletContext(), request, response,   
  92.                 _lifecycle);   
  93.         try {   
  94.             _lifecycle.execute(facesContext);   
  95.             _lifecycle.render(facesContext);   
  96.         } catch (Throwable e) {   
  97.             if (e instanceof IOException) {   
  98.                 throw (IOException) e;   
  99.             } else if (e instanceof ServletException) {   
  100.                 throw (ServletException) e;   
  101.             } else if (e.getMessage() != null) {   
  102.                 throw new ServletException(e.getMessage(), e);   
  103.             } else {   
  104.                 throw new ServletException(e);   
  105.             }   
  106.         } finally {   
  107.             facesContext.release();   
  108.         }   
  109.         if (log.isTraceEnabled())   
  110.             log.trace("service end");   
  111.     }   
  112. }