JSF 源代码赏析之FacesServlet
学习JSF 多日,现在开始看看源代码。
首先是FacesServlet类了,作为一个前端控制器,每一个JSF请求都要通过FacesServlet,然后再到其他阶段,那么,FacesServlet 到底做了些什么操作呢?
- /**
- *
FacesServlet is a servlet that manages the request
- * processing lifecycle for web applications that are utilizing JavaServer
- * Faces to construct the user interface.
- */
头部注释说的很明白,管理请求的处理周期。至于怎么管理,下面先来看一看到底声明了什么变量
- public static final String CONFIG_FILES_ATTR =
- "javax.faces.CONFIG_FILES";
- public static final String LIFECYCLE_ID_ATTR =
- "javax.faces.LIFECYCLE_ID";
- private static final Logger LOGGER =
- Logger.getLogger("javax.faces.webapp", "javax.faces.LogStrings");
- private FacesContextFactory facesContextFactory = null;
- private Lifecycle lifecycle = null;
- 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对象提供了下面的方法:
- public abstract FacesContext getFacesContext
- (Object context, Object request,
- Object response, Lifecycle lifecycle)
- throws FacesException;
来产生FacesContext对象,并且这是一个抽象方法,如何调用,则是JSF实现的事情了,并且FacesContextFactory会为每一个Request请求返回一个FacesContext对象。注意,这里用的是“返回”,而不是生成,是因为FacesContextFactory并不一定会为每一个请求生成一个新的FacesContext对象,FacesContext对象有一个release方法,这个方法负责释放FacesContext的资源,在调用这个方法之前,通过FacesContext.getCurrentInstance可以返回当前线程上的实例,这样实现FacesContext在某种程度上的重用和pool。
下面应该来看看在FacesServlet中如何调用FacesContextFactory来产生一个FacesContext对象了。
首先要产生一个FacesContextFactory对象,这是通过FacesServlet的init方法来实现的:
- public void init(ServletConfig servletConfig) throws ServletException {
- // Save our ServletConfig instance
- this.servletConfig = servletConfig;
- // Acquire our FacesContextFactory instance
- try {
- facesContextFactory = (FacesContextFactory)
- FactoryFinder.getFactory
- (FactoryFinder.FACES_CONTEXT_FACTORY);
- } catch (FacesException e) {
- ResourceBundle rb = LOGGER.getResourceBundle();
- String msg = rb.getString("severe.webapp.facesservlet.init_failed");
- Throwable rootCause = (e.getCause() != null) ? e.getCause() : e;
- LOGGER.log(Level.SEVERE, msg, rootCause);
- throw new UnavailableException(msg);
- }
- // Acquire our Lifecycle instance
- try {
- LifecycleFactory lifecycleFactory = (LifecycleFactory)
- FactoryFinder.getFactory(FactoryFinder.LIFECYCLE_FACTORY);
- String lifecycleId ;
- // First look in the servlet init-param set
- if (null == (lifecycleId = servletConfig.getInitParameter(LIFECYCLE_ID_ATTR))) {
- // If not found, look in the context-param set
- lifecycleId = servletConfig.getServletContext().getInitParameter
- (LIFECYCLE_ID_ATTR);
- }
- if (lifecycleId == null) {
- lifecycleId = LifecycleFactory.DEFAULT_LIFECYCLE;
- }
- lifecycle = lifecycleFactory.getLifecycle(lifecycleId);
- } catch (FacesException e) {
- Throwable rootCause = e.getCause();
- if (rootCause == null) {
- throw e;
- } else {
- throw new ServletException(e.getMessage(), rootCause);
- }
- }
- }
在这个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,整个过程是这样的:
- // Acquire our Lifecycle instance
- try {
- LifecycleFactory lifecycleFactory = (LifecycleFactory)
- FactoryFinder.getFactory(FactoryFinder.LIFECYCLE_FACTORY);
- String lifecycleId ;
- // First look in the servlet init-param set
- if (null == (lifecycleId = servletConfig.getInitParameter(LIFECYCLE_ID_ATTR))) {
- // If not found, look in the context-param set
- lifecycleId = servletConfig.getServletContext().getInitParameter
- (LIFECYCLE_ID_ATTR);
- }
- if (lifecycleId == null) {
- lifecycleId = LifecycleFactory.DEFAULT_LIFECYCLE;
- }
- lifecycle = lifecycleFactory.getLifecycle(lifecycleId);
- } catch (FacesException e) {
- Throwable rootCause = e.getCause();
- if (rootCause == null) {
- throw e;
- } else {
- throw new ServletException(e.getMessage(), rootCause);
- }
- }
通过加载不同实现的LifecycleFactory对象,就可以允许加载不同的Lifecycle对象,这对于扩展JSF的功能是非常重要的,
当没有显示表明lifecycleId时,lifecycleFactory就会加载默认的lifecycleId,并根据lifecycleId加载Lifecycle对象
Lifecycle类负责JSF请求处理的全过程,主要是通过执行其中的execute方法和render方法实现的,FacesServlet的service方法很好的说明了这一点:
- public void service(ServletRequest request,
- ServletResponse response)
- throws IOException, ServletException {
- // If prefix mapped, then ensure requests for /WEB-INF are
- // not processed.
- String pathInfo = ((HttpServletRequest) request).getPathInfo();
- if (pathInfo != null) {
- pathInfo = pathInfo.toUpperCase();
- if (pathInfo.startsWith("/WEB-INF/")
- || pathInfo.equals("/WEB-INF")
- || pathInfo.startsWith("/META-INF/")
- || pathInfo.equals("/META-INF")) {
- ((HttpServletResponse) response).
- sendError(HttpServletResponse.SC_NOT_FOUND);
- return;
- }
- }
- // Acquire the FacesContext instance for this request
- FacesContext context = facesContextFactory.getFacesContext
- (servletConfig.getServletContext(), request, response, lifecycle);
- // Execute the request processing lifecycle for this request
- try {
- lifecycle.execute(context);
- lifecycle.render(context);
- } catch (FacesException e) {
- Throwable t = e.getCause();
- if (t == null) {
- throw new ServletException(e.getMessage(), e);
- } else {
- if (t instanceof ServletException) {
- throw ((ServletException) t);
- } else if (t instanceof IOException) {
- throw ((IOException) t);
- } else {
- throw new ServletException(t.getMessage(), t);
- }
- }
- }
- finally {
- // Release the FacesContext instance for this request
- context.release();
- }
- }
好了,FacesServlet的源码我们就看到这里,下一篇中我们将深入研究Lifecycle对象的执行过程,在最后,就让我们用myFaces的FacesServlet实现来结束吧:
- public final class FacesServlet implements Servlet {
- private static final Log log = LogFactory.getLog(FacesServlet.class);
- public static final String CONFIG_FILES_ATTR = "javax.faces.CONFIG_FILES";
- public static final String LIFECYCLE_ID_ATTR = "javax.faces.LIFECYCLE_ID";
- private static final String SERVLET_INFO = "FacesServlet of the MyFaces API implementation";
- private ServletConfig _servletConfig;
- private FacesContextFactory _facesContextFactory;
- private Lifecycle _lifecycle;
- public FacesServlet() {
- super();
- }
- public void destroy() {
- _servletConfig = null;
- _facesContextFactory = null;
- _lifecycle = null;
- if (log.isTraceEnabled())
- log.trace("destroy");
- }
- public ServletConfig getServletConfig() {
- return _servletConfig;
- }
- public String getServletInfo() {
- return SERVLET_INFO;
- }
- private String getLifecycleId() {
- String lifecycleId = _servletConfig.getServletContext()
- .getInitParameter(LIFECYCLE_ID_ATTR);
- return lifecycleId != null ? lifecycleId
- : LifecycleFactory.DEFAULT_LIFECYCLE;
- }
- public void init(ServletConfig servletConfig) throws ServletException {
- if (log.isTraceEnabled())
- log.trace("init begin");
- _servletConfig = servletConfig;
- _facesContextFactory = (FacesContextFactory) FactoryFinder
- .getFactory(FactoryFinder.FACES_CONTEXT_FACTORY);
- // TODO: null-check for Weblogic, that tries to initialize Servlet
- // before ContextListener
- // Javadoc says: Lifecycle instance is shared across multiple
- // simultaneous requests, it must be implemented in a thread-safe
- // manner.
- // So we can acquire it here once:
- LifecycleFactory lifecycleFactory = (LifecycleFactory) FactoryFinder
- .getFactory(FactoryFinder.LIFECYCLE_FACTORY);
- _lifecycle = lifecycleFactory.getLifecycle(getLifecycleId());
- if (log.isTraceEnabled())
- log.trace("init end");
- }
- public void service(ServletRequest request, ServletResponse response)
- throws IOException, ServletException {
- HttpServletRequest httpRequest = ((HttpServletRequest) request);
- String pathInfo = httpRequest.getPathInfo();
- // if it is a prefix mapping ...
- if (pathInfo != null
- && (pathInfo.startsWith("/WEB-INF") || pathInfo
- .startsWith("/META-INF"))) {
- StringBuffer buffer = new StringBuffer();
- buffer.append(" Someone is trying to access a secure resource : "
- + pathInfo);
- buffer
- .append("/n remote address is "
- + httpRequest.getRemoteAddr());
- buffer.append("/n remote host is " + httpRequest.getRemoteHost());
- buffer.append("/n remote user is " + httpRequest.getRemoteUser());
- buffer.append("/n request URI is " + httpRequest.getRequestURI());
- log.warn(buffer.toString());
- // Why does RI return a 404 and not a 403, SC_FORBIDDEN ?
- ((HttpServletResponse) response)
- .sendError(HttpServletResponse.SC_NOT_FOUND);
- return;
- }
- if (log.isTraceEnabled())
- log.trace("service begin");
- FacesContext facesContext = _facesContextFactory.getFacesContext(
- _servletConfig.getServletContext(), request, response,
- _lifecycle);
- try {
- _lifecycle.execute(facesContext);
- _lifecycle.render(facesContext);
- } catch (Throwable e) {
- if (e instanceof IOException) {
- throw (IOException) e;
- } else if (e instanceof ServletException) {
- throw (ServletException) e;
- } else if (e.getMessage() != null) {
- throw new ServletException(e.getMessage(), e);
- } else {
- throw new ServletException(e);
- }
- } finally {
- facesContext.release();
- }
- if (log.isTraceEnabled())
- log.trace("service end");
- }
- }
JSF 源代码赏析之FacesServlet相关推荐
- JSF 源代码赏析之Lifecycle
JSF 源代码赏析之Lifecycle 关键字: jsf sourcecode lifecycle JSF的生命周期在JSF应用中起着至关重要的作用,每一个JSF请求的处理都需要经过一次生命周期,本文 ...
- 代码之美——Doom3源代码赏析2
http://www.csdn.net/article/2013-01-17/2813778-the-beauty-of-doom3-source-code/2 摘要:Dyad作者.资深C++工程师S ...
- 代码之美——Doom3源代码赏析
摘要:Dyad作者.资深C++工程师Shawn McGrathz在空闲时翻看了Doom3的源代码,发出了这样的惊叹:"这是我见过的最整洁.最优美的代码!""Doom 3的 ...
- Unity引擎及编辑器C#源代码赏析(一)—目录结构
前言 3月23号,Unity在GitHub上发布了引擎和编辑器的C#源代码,供Unity开发者学习参考使用.笔者第一时间去GitHub上把源码下载下来看了一下,虽然Unity仅仅是开源了C#这一层代码 ...
- 转:代码之美——Doom3源代码赏析
背景介绍: Doom3是id Software于2004年开发的第一人称射击游戏,目前以GPL v3协议开源.其采用游戏引擎的是id Tech 4,由id Software创始人.首席程序员John ...
- 代码之美——Doom3源代码赏析1
http://www.csdn.net/article/2013-01-17/2813778-the-beauty-of-doom3-source-code/1 摘要:Dyad作者.资深C++工程师S ...
- Inside Dynamics Axapta源代码赏析(五)
第十二章 The Database layer 1.更改RecVersionAX4.0使用了乐观并发控制,RecVersion是用来标识记录版本的. static void RecVersionCha ...
- Inside Dynamics Axapta源代码赏析(四)
第八章:Developing Applications Using Business Connector 这一章的代码主要演示如何通过Business Connector与Axapta交互 在Dyna ...
- Inside Dynamics Axapta源代码赏析(三)
第七章:Extending Dynamics Ax 使用该章的某些工程前需要在 系统管理->设置->电子邮件参数处 设置好相关的参数,当然如果不想用示例代码中的发邮件功能,可以将其注释掉 ...
最新文章
- 火狐中H1到H5都有特定margn
- 【设计模式】软件设计七大原则 ( 里氏替换原则 | 定义 | 定义扩展 | 引申 | 意义 | 优点 )
- 保存Activity的状态
- C语言、嵌入式位操作精华技巧大汇总
- C++面试题目(五)
- 项目实战|100个蓝牙接收器发货了
- 听课评课记录计算机应用,教师听课的评语(精选10篇)
- SpringBoot脚手架工程快速搭建
- Windows 下使用GNU开发环境[转]
- 30美元攻陷Intel SGX enclave,Intel 不打算修复
- 关于计算机优点缺点的英语作文,跪求一篇英语作文 题目:论计算机的优缺点...
- oracle有三种类型的异常错误: 预定义 ( Predefined )错误里面的常见错误
- poj 2355 Railway tickets 很纯的dp
- 音痴测试软件,写歌软件有哪些,推荐一款能拯救音痴的软件
- 景联文科技入选全国信息技术标准化技术委员会生物特征识别技术委员会
- 为大家准备一份数据分析师简历的清单
- 防沉迷与身份证系统挂钩 网游要实名认证
- java根据经纬度转地址或者根据地址转经纬度
- 机电和计算机专业怎么选,计算机专业怎么选城口_竟成学校
- python Xarray处理设置2维数组作为coordinates
热门文章
- .net后台怎么提取html中的多个图片的绝对地址_SpringBoot中yml配置文件说明和一些常用配置项说明...
- 有关使用sklearn LogisticRegression出现的 DeprecationWarning:
- 计算机网络之传输层:3、TCP协议
- 9-4:C++多态之单继承和多继承中的虚函数表
- Zookeeper C 回调函数
- VirtualAllocEx 跨进程读写数据 代码注入
- 496. 下一个更大元素 I/503. 下一个更大元素 II/739. 每日温度
- no crontab for root 问题解决方法
- Python pip install 包后,pycharm仍然提示No module named(提示 No module named ‘pip‘)
- Qt:Windows编程—DLL注入与卸载