转载自:http://www.ibm.com/developerworks/cn/java/l-struts1-1/
摘要:作为基于 MVC 模式的 Web 应用最经典框架,Struts 已经正式推出了 1.1 版本,该版本在以往版本的基础上,提供了许多激动人心的新功能。本文就将带你走进 Struts 1.1 去深入地了解这些功能。
Model 2
Struts 是基于 Model 2 之上的,而 Model 2 是经典的 MVC(模型-视图-控制器)模型的 Web 应用变体,这个改变主要是由于网络应用的特性 --HTTP 协议的无状态性引起的。Model 2 的目的和 MVC 一样,也是利用控制器来分离模型和视图,达到一种层间松散耦合的效果,提高系统灵活性、复用性和可维护性。在多数情况下,你可以将 Model 2 与 MVC 等同起来。
下图表示一个基于 Java 技术的典型网络应用,从中可以看出 Model 2 中的各个部分是如何对应于 Java 中各种现有技术的。
在利用 Model 2 之前,我们是把所有的表示逻辑和业务逻辑都集中在一起(比如大杂烩似的 JSP),有时也称这种应用模式为 Model 1,Model 1 的主要缺点就是紧耦合,复用性差以及维护成本高。
Struts 1.1 和 Model 2
既然 Struts 1.1 是基于 Model 2 之上,那它的底层机制也就是 MVC,下面是 Struts 1.1 中的 MVC 实现示意图:
图解说明:其中不同颜色代表 MVC 的不同部分:红色(控制器)、紫色(模型)和绿色(视图)
首先,控制器(ActionServlet)进行初始化工作,读取配置文件(struts-config.xml),为不同的 Struts 模块初始化相应的 ModuleConfig 对象。比如配置文件中的 Action 映射定义都保存在 ActionConfig 集合中。相应地有 ControlConfig 集合、FormBeanConfig 集合、ForwardConfig 集合和 MessageResourcesConfig 集合等。
提示:模块是在 Struts 1.1 中新提出的概念,在稍后的内容中我们将详细介绍,你现在可以简单地把模块看作是一个子系统,它们共同组成整个应用,同时又各自独立。Struts 1.1 中所有的处理都是在特定模块环境中进行的。模块的提出主要是为了解决 Struts 1.0 中单配置文件的问题。
控制器接收 HTTP 请求,并从 ActionConfig 中找出对应于该请求的 Action 子类,如果没有对应的 Action,控制器直接将请求转发给 JSP 或者静态页面。否则控制器将请求分发至具体 Action 类进行处理。
在控制器调用具体 Action 的 execute 方法之前,ActionForm 对象将利用 HTTP 请求中的参数来填充自己(可选步骤,需要在配置文件中指定)。具体的 ActionForm 对象应该是 ActionForm 的子类对象,它其实就是一个 JavaBean。此外,还可以在 ActionForm 类中调用 validate 方法来检查请求参数的合法性,并且可以返回一个包含所有错误信息的 ActionErrors 对象。如果执行成功,ActionForm 自动将这些参数信息以 JavaBean(一般称之为 form bean)的方式保存在 Servlet Context 中,这样它们就可以被其它 Action 对象或者 JSP 调用。
Struts 将这些 ActionForm 的配置信息都放在 FormBeanConfig 集合中,通过它们 Struts 能够知道针对某个客户请求是否需要创建相应的 ActionForm 实例。
Action 很简单,一般只包含一个 execute 方法,它负责执行相应的业务逻辑,如果需要,它也进行相应的数据检查。执行完成之后,返回一个 ActionForward 对象,控制器通过该 ActionForward 对象来进行转发工作。我们主张将获取数据和执行业务逻辑的功能放到具体的 JavaBean 当中,而 Action 只负责完成与控制有关的功能。遵循该原则,所以在上图中我将 Action 对象归为控制器部分。
提示:其实在 Struts 1.1 中,ActionMapping 的作用完全可以由 ActionConfig 来替代,只不过由于它是公共 API 的一部分以及兼容性的问题得以保留。ActionMapping 通过继承 ActionConfig 来获得与其一致的功能,你可以等同地看待它们。同理,其它例如 ActionForward 与 ForwardConfig 的关系也是如此。
下图给出了客户端从发出请求到获得响应整个过程的图解说明。
下面我们就来详细地讨论一下其中的每个部分,在这之前,先来了解一下模块的概念。
模块
我们知道,在 Struts 1.0 中,我们只能在 web.xml 中为 ActionServlet 指定一个配置文件,这对于我们这些网上的教学例子来说当然没什么问题,但是在实际的应用开发过程中,可能会有些麻烦。因为许多开发人员都可能同时需要修改配置文件,但是配置文件只能同时被一个人修改,这样肯定会造成一定程度上的资源争夺,势必会影响开发效率和引起开发人员的抱怨。
在 Struts 1.1 中,为了解决这个并行开发的问题,提出了两种解决方案:
- 多个配置文件的支持
- 模块的支持
支持多个配置文件,是指你能够为 ActionServlet 同时指定多个 xml 配置文件,文件之间以逗号分隔,比如 Struts 提供的 MailReader 演示例子中就采用该种方法。
<!-- Action Servlet Configuration --> <servlet> <servlet-name>action</servlet-name> <servlet-class>org.apache.struts.action.ActionServlet</servlet-class> <init-param> <param-name>config</param-name> <param-value> /WEB-INF/struts-config.xml, /WEB-INF/struts-config-registration.xml </param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet>
这种方法可以很好地解决修改冲突的问题,不同的开发人员可以在不同的配置文件中设置自己的 Action、ActionForm 等等(当然不是说每个开发人员都需要自己的配置文件,可以按照系统的功能模块进行划分)。但是,这里还是存在一个潜在的问题,就是可能不同的配置文件之间会产生冲突,因为在 ActionServlet 初始化的时候这几个文件最终还是需要合并到一起的。比如,在 struts-config.xml 中配置了一个名为 success 的 <forward>,而在 struts-config-registration.xml 中也配置了一个同样的 <forward>,那么执行起来就会产生冲突。
为了彻底解决这种冲突,Struts 1.1 中引进了模块(Module)的概念。一个模块就是一个独立的子系统,你可以在其中进行任意所需的配置,同时又不必担心和其它的配置文件产生冲突。因为前面我们讲过,ActionServlet 是将不同的模块信息保存在不同的 ModuleConfig 对象中的。要使用模块的功能,需要进行以下的准备工作:
1、为每个模块准备一个配置文件
2、配置 web.xml 文件,通知控制器
决定采用多个模块以后,你需要将这些信息告诉控制器,这需要在 web.xml 文件进行配置。下面是一个典型的多模块配置:
<init-param> <param-name>config</param-name> <param-value>/WEB-INF/struts-config.xml</param-value> </init-param> <init-param> <param-name>config/customer</param-name> <param-value>/WEB-INF/struts-config-customer.xml</param-value> </init-param> <init-param> <param-name>config/order</param-name> <param-value>/WEB-INF/struts-config-order.xml</param-value> </init-param>
要配置多个模块,你需要在原有的一个 <init-param>(在 Struts 1.1 中将其对应的模块称为缺省模块)的基础之上,增加模块对应的 <init-param>。其中 <param-name> 表示为 config/XXX 的形式,其中 XXX 为对应的模块名,<param-value> 中还是指定模块对应的配置文件。上面这个例子说明该应用有三个模块,分别是缺省模块、customer 和 order,它们分别对应不同的配置文件。
3、准备各个模块所需的 ActionForm、Action 和 JSP 等资源
但是要注意的是,模块的出现也同时带来了一个问题,即如何在不同模块间进行转发?有两种方法可以实现模块间的转发,一种就是在 <forward>(全局或者本地)中定义,另外一种就是利用 org.apache.struts.actions.SwitchAction。
下面就是一个全局的例子:
... <struts-config> ... <global-forwards> <forward name="toModuleB" contextRelative="true" path="/moduleB/index.do" redirect="true"/> ... </global-forwards> ... </struts-config>
可以看出,只需要在原有的 path 属性前加上模块名,同时将 contextRelative 属性置为 true 即可。此外,你也可以在 <action> 中定义一个类似的本地 <forward>。
<action-mappings> <!-- Action mapping for profile form --> <action path="/login" type="com.ncu.test.LoginAction" name="loginForm" scope="request" input="tile.userLogin" validate="true"> <forward name="success" contextRelative="true" path="/moduleA/login.do"/> </action> </action-mappings>
如果你已经处在其他模块,需要转回到缺省模块,那应该类似下面这样定义,即模块名为空。
<forward name="success" contextRelative="true" path="/login.do"/>
此外,你也可以使用 org.apache.struts.actions.SwitchAction,例如:
... <action-mappings> <action path="/toModule" type="org.apache.struts.actions.SwitchAction"/> ... </action-mappings> ...
ActionServlet
我们首先来了解 MVC 中的控制器。在 Struts 1.1 中缺省采用 ActionServlet 类来充当控制器。当然如果 ActionServlet 不能满足你的需求,你也可以通过继承它来实现自己的类。这可以在 /WEB-INF/web.xml 中来具体指定。
要掌握 ActionServlet,就必须了解它所扮演的角色。首先,ActionServlet 表示 MVC 结构中的控制器部分,它需要完成控制器所需的前端控制及转发请求等职责。其次,ActionServlet 被实现为一个专门处理 HTTP 请求的 Servlet,它同时具有 servlet 的特点。在 Struts 1.1 中它主要完成以下功能:
- 接收客户端请求
- 根据客户端的 URI 将请求映射到一个相应的 Action 类
- 从请求中获取数据填充 Form Bean(如果需要)
- 调用 Action 类的 execute() 方法获取数据或者执行业务逻辑
- 选择正确的视图响应客户
此外,ActionServlet 还负责初始化和清除应用配置信息的任务。ActionServlet 的初始化工作在 init 方法中完成,它可以分为两个部分:初始化 ActionServlet 自身的一些信息以及每个模块的配置信息。前者主要通过 initInternal、initOther 和 initServlet 三个方法来完成。
我们可以在 /WEB-INF/web.xml 中指定具体的控制器以及初始参数,由于版本的变化以及 Struts 1.1 中模块概念的引进,一些初始参数被废弃或者移入到 /WEB-INF/struts-config.xml 中定义。下面列出所有被废弃的参数,相应地在 web.xml 文件中也不鼓励再使用。
- application
- bufferSize
- content
- debug
- factory
- formBean
- forward
- locale
- mapping
- maxFileSize
- multipartClass
- nocache
- null
- tempDir
ActionServlet 根据不同的模块来初始化 ModuleConfig 类,并在其中以 XXXconfig 集合的方式保存该模块的各种配置信息,比如 ActionConfig,FormBeanConfig 等。
初始化工作完成之后,ActionServlet 准备接收客户请求。针对每个请求,方法 process(HttpServletRequest request, HttpServletResponse response) 将被调用。该方法指定具体的模块,然后调用该模块的 RequestProcessor 的 process 方法。
protected void process(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { RequestUtils.selectModule(request, getServletContext()); getRequestProcessor(getModuleConfig(request)).process(request, response); }
RequestProcessor 包含了 Struts 控制器的所有处理逻辑,它调用不同的 processXXX 方法来完成不同的处理。下表列出其中几个主要的方法:
方法 | 功能 |
---|---|
processPath | 获取客户端的请求路径 |
processMapping | 利用路径来获得相应的 ActionMapping |
processActionForm | 初始化 ActionForm(如果需要)并存入正确的 scope 中 |
processActionCreate | 初始化 Action |
processActionPerform | 调用 Action 的 execute 方法 |
processForwardConfig | 处理 Action 返回的 ActionForward |
ActionForm
对于 ActionForm 你可以从以下几个方面来理解它:
- ActionForm 表示 HTTP 窗体中的数据,可以将其看作是模型和视图的中介,它负责保存视图中的数据供模型或者视图使用。Struts 1.1 文档中把它比作 HTTP 和 Action 之间的防火墙,这体现了 ActionForm 具有的过滤保护的作用,只有通过 ActionForm 验证的数据才能够发送到 Action 处理。
- ActionForm 是与一个或多个 ActionConfig 关联的 JavaBean,在相应的 action 的 execute 方法被调用之前,ActionForm 会自动利用请求参数来填充自己(初始化属性)。
- ActionForm 是一个抽象类,你必须通过继承来实现自己的类。
ActionForm 首先利用属性的 getter 和 setter 方法来实现初始化,初始化完毕后,ActionForm 的 validate 方法被调用,你可以在其中来检查请求参数的正确性和有效性,并且可以将错误信息以 ActionErrors 的形式返回到输入窗体。否则,ActionForm 将被作为参数传给 action 的 execute 方法以供使用。
ActionForm bean 的生命周期可以设置为 session(缺省)和 request,当设置为 session 时,记得在 reset 方法中将所有的属性重新设置为初始值。
由于 ActionForm 对应于 HTTP 窗体,所以随着页面的增多,你的 ActionForm 将会急速增加。而且可能同一类型页面字段将会在不同的 ActionForm 中出现,并且在每个 ActionForm 中都存在相同的验证代码。为了解决这个问题,你可以为整个应用实现一个 ActionForm 或者至少一个模块对应于一个 ActionForm。
但是,聚合的代价就是复用性很差,而且难维护。针对这个问题,在 Struts 1.1 中提出了 DynaActionForm 的概念。
DynaActionForm 类
DynaActionForm 的目的就是减少 ActionForm 的数目,利用它你不必创建一个个具体的 ActionForm 类,而是在配置文件中配置出所需的虚拟 ActionForm。例如,在下表中通过指定 <form-bean> 的 type 为"org.apache.struts.action.DynaActionForm"来创建一个动态的 ActionForm--loginForm。
<form-beans> <form-bean name="loginForm" type="org.apache.struts.action.DynaActionForm"> <form-property name="actionClass" type="java.lang.String"/> <form-property name="username" type="java.lang.String"/> <form-property name="password" type="java.lang.String"/> </form-bean> </form-beans>
动态的 ActionForm 的使用方法跟普通的 ActionForm 相同,但是要注意一点。普通的 ActionForm 对象需要为每个属性提供 getter 和 setter 方法,以上面的例子而言,我们需要提供 getUsername() 和 setUsername() 方法取得和设置 username 属性,同样地有一对方法用于取得和设置 password 属性和 actionClass 属性。
如果使用 DynaActionForm,它将属性保存在一个 HashMap 类对象中,同时提供相应的 get(name) 和 set(name) 方法,其中参数 name 是要访问的属性名。例如要访问 DynaActionForm 中 username 的值,可以采用类似的代码:
String username = (String)form.get("username");
由于值存放于一个 HashMap 对象,所以要记得对 get() 方法返回的 Object 对象做强制性类型转换。正是由于这点区别,如果你在 Action 中非常频繁地使用 ActionForm 对象,建议还是使用普通的 ActionForm 对象。
在 Struts 1.1 中,除了 DynaActionForm 以外,还提供了表单输入自动验证的功能,在包 org.apache.struts.validator 中提供了许多有用的类,其中最常见的就是 DynaValidatorForm 类。
DynaValidatorForm 类
DynaValidatorForm 是 DynaActionForm 的子类,它能够提供动态 ActionForm 和自动表单输入验证的功能。和使用 DynaActionForm 类似,你必须首先在配置文件中进行配置:
<form-beans> <form-bean name="loginForm" type="org.apache.struts.validator.DynaValidatorForm"> <form-property name="actionClass" type="java.lang.String"/> <form-property name="username" type="java.lang.String"/> <form-property name="password" type="java.lang.String"/> </form-bean> </form-beans>
同时要定义验证的插件:
<plug-in className="org.apache.struts.validator.ValidatorPlugIn"> <set-property property="pathnames" value="/WEB-INF/validator-rules.xml, /WEB-INF/validation.xml"/> </plug-in>
其中的 validator.xml 和 validator-rules.xml 分别表示验证定义和验证规则的内容(可以合并在一起),比如针对上例中的 DynaValidatorForm,我们有如下验证定义(validator.xml):
<?xml version="1.0" encoding="ISO-8859-1" ?> <!DOCTYPE form-validation PUBLIC "-//Apache Software Foundation//DTD Commons Validator Rules Configuration 1.0//EN" "http://jakarta.apache.org/commons/dtds/validator_1_0.dtd"> <!-- Validation Rules $Id: validation.xml--> <form-validation> <!-- ========== Default Language Form Definitions ===================== --> <formset> <form name="loginForm"> <field property="username" depends="required, minlength,maxlength"> <arg0 key="prompt.username"/> <arg1 key="${var:minlength}" name="minlength" resource="false"/> <arg2 key="${var:maxlength}" name="maxlength" resource="false"/> <var> <var-name>maxlength</var-name> <var-value>16</var-value> </var> <var> <var-name>minlength</var-name> <var-value>3</var-value> </var> </field> <field property="password" depends="required, minlength,maxlength" bundle="alternate"> <arg0 key="prompt.password"/> <arg1 key="${var:minlength}" name="minlength" resource="false"/> <arg2 key="${var:maxlength}" name="maxlength" resource="false"/> <var> <var-name>maxlength</var-name> <var-value>16</var-value> </var> <var> <var-name>minlength</var-name> <var-value>3</var-value> </var> </field> </form> </formset> </form-validation>
从上述定义中,我们可以看到对于字段 username 有三项验证:required, minlength, maxlength,意思是该字段不能为空,而且长度在 3 和 16 之间。而 validator-rules.xml 文件则可以采用 Struts 提供的缺省文件。注意在 <form-bean> 中定义的 form 是如何与 validation.xml 中的 form 关联起来的。最后,要启动自动验证功能,还需要将 Action 配置的 validate 属性设置为 true。
<action path="/login" type="com.ncu.test.LoginAction" name="loginForm" scope="request" input="tile.userLogin"validate="true">
此时,Struts 将根据 xml 配置文件中的定义来检验表单输入,并将不符合要求的错误信息输出到页面。但是你可能会想:这个功能虽然好,可是什么检验都跑到服务器端执行,效率方面和用户易用性方面是不是有些问题?你可能会怀念起那简单的 JavaScript 客户端验证。
不用担心,在 Struts 1.1 中也支持 JavaScript 客户端验证。如果你选择了客户端验证,当某个表单被提交以后,Struts 1.1 启动客户端验证,如果浏览器不支持 JavaScript 验证,则服务器端验证被启动,这种双重验证机制能够最大限度地满足各种开发者的需要。JavaScript 验证代码也是在 validator-rules.xml 文件中定义的。要启动客户端验证,你必须在相应的 JSP 文件中做如下设置:
- 为 <html:form> 增加 onsubmit 属性
- 设置 Javascript 支持
下表中列出了一 JSP 文件的示例代码,红字部分为 Javascript 验证所需代码。
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %> <table bgcolor="#9AFF9A" cellspacing="0" cellpadding="10" border="1" width="100%"> <tr> <td> <table cellspacing="0" cellpadding="0" border="0" width="100%"> <tr bgcolor="#696969"> <td align="center"> <font color="#FFFFFF">Panel 3: Profile</font> </td> </tr> <tr> <td><br> <html:errors/> <html:form action="/login.do" focus="username" onsubmit="return validateLoginForm(this);"> <html:hidden property="actionClass"/> <center> <table> <tr> <td>UserName:</td> <td><html:text property="username" size="20"/></td> </tr> <tr> <td>Password:</td> <td><html:password property="password" size="20"/></td> </tr> <tr> <td colspan=2><html:submit property="submitProperty" value="Submit"/></td> </table> </center> </html:form> <html:javascript formName="loginForm" dynamicJavascript="true" staticJavascript="false"/> <script language="Javascript1.1" src="staticJavascript.jsp"></script> </td> </tr> </table> </td> </tr> </table>
其中 onsubmit 的值为"return validateLoginForm(this);",它的语法为:
return validate + struts-config.xml 中定义的 form-bean 名称 + (this);
staticJavascript.jsp 的内容为:
<%@ page language="java" %> <%-- set document type to Javascript (addresses a bug in Netscape according to a web resource --%> <%@ page contentType="application/x-javascript" %> <%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %> <html:javascript dynamicJavascript="false" staticJavascript="true"/>
如果 validator-rules.xml 中定义的基本验证功能不能满足你的需求,你可以自己添加所需的验证类型。
Action
我们通过继承 Action 类来实现具体的执行类。具体 Action 类的功能一般都在 execute(以前是 perform 方法)方法中完成,其中主要涉及到以下几个方面:
- 辅助 ActionForm 进行一些表单数据的检查。
- 执行必要的业务逻辑,比如存取数据库,调用实体 bean 等。
- 更新服务器端的 bean 数据,后续对象中可能会用到这些数据,比如在 JSP 中利用 bean:write 来获得这些数据。
- 根据处理结果决定程序的去处,并以 ActionForward 对象的形式返回给 ActionServlet。
提示:由于在 Action 和 ActionForm 中都可以实现验证方法,那么如何来安排它们之间的分工呢?一般来说,我们秉着 MVC 分离的原则,也就是视图级的验证工作放在 ActionForm 来完成,比如输入不能为空,email 格式是否正确,利用 ValidatorForm 可以很轻松地完成这些工作。而与具体业务相关的验证则放入 Action 中,这样就可以获得最大 ActionForm 重用性的可能。
前面我们提到过,我们主张将业务逻辑执行分离到单独的 JavaBean 中,而 Action 只负责错误处理和流程控制。而且考虑到重用性的原因,在执行业务逻辑的 JavaBean 中不要引用任何与 Web 应用相关的对象,比如 HttpServletRequest,HttpServletResponse 等对象,而应该将其转化为普通的 Java 对象。关于这一点,可以参考 Petstore 中 WAF 框架的实现思路。
此外,你可能还注意到 execute 与 perform 的一个区别:execute 方法简单地掷出 Exception 异常,而 perform 方法则掷出 ServletException 和 IOException 异常。这不是说 Struts 1.1 在异常处理功能方面弱化了,而是为了配合 Struts 1.1 中一个很好的功能 -- 宣称式异常处理机制。
宣称式异常处理
和 EJB 中的宣称式事务处理概念类似,宣称式异常处理其实就是可配置的异常处理,你可以在配置文件中指定由谁来处理 Action 类中掷出的某种异常。你可以按照以下步骤来完成该功能:
- 实现 org.apache.struts.action.ExceptionHandler 的子类,覆盖 execute 方法,在该方法中处理异常并且返回一个 ActionForward 对象
- 在配置文件中配置异常处理对象,你可以配置一个全局的处理类或者单独为每个 Action 配置处理类
下表就定义了一个全局的处理类 CustomizedExceptionHandler,它被用来处理所有的异常。
<global-exceptions> <exception handler="com.yourcorp.CustomizedExceptionHandler" key="global.error.message" path="/error.jsp" scope="request" type="java.lang.Exception"/> </global-exceptions>
其中具体的参数含义,可以参考 ExceptionHandler.java 源文件。
taglib
讲完了模型和控制器,接下来我们要涉及的是视图。视图的角色主要是由 JSP 来完成,从 JSP 的规范中可以看出,在视图层可以"折腾"的技术不是很多,主要的就是自定义标记库的应用。Struts 1.1 在原有的四个标记库的基础上新增了两个标记库 --Tiles 和 Nested。
其中 Tiles 除了替代 Template 的基本模板功能外,还增加了布局定义、虚拟页面定义和动态页面生成等功能。Tiles 强大的模板功能能够使页面获得最大的重用性和灵活性,此外可以结合 Tiles 配置文件中的页面定义和 Action 的转发逻辑,即你可以将一个 Action 转发到一个在 Tiles 配置文件中定义的虚拟页面,从而减少页面的数量。比如,下表中的 Action 定义了一个转发路径,它的终点是 tile.userMain,而后者是你在 Tiles 配置文件中定义的一个页面。
<!-- ========== Action Mapping Definitions ============================== --> <action-mappings> <!-- Action mapping for profile form --> <action path="/login" type="com.ncu.test.LoginAction" name="loginForm" scope="request" input="tile.userLogin" validate="true"> <forward name="success" path="tile.userMain"/> </action> </action-mappings>
Tiles 配置文件:tiles-defs.xml
<!DOCTYPE tiles-definitions PUBLIC "-//Apache Software Foundation//DTD Tiles Configuration//EN" "http://jakarta.apache.org/struts/dtds/tiles-config.dtd"> <tiles-definitions> <!-- ======================================================= --> <!-- Master definitions --> <!-- ======================================================= --> <!-- Page layout used as root for all pages. --> <definition name="rootLayout" path="/tiles-layouts/rootLayout.jsp"> <put name="titleString" value="CHANGE-ME"/> <put name="topMenu" value="/tiles-components/topMenu.jsp"/> <put name="leftMenu" value="/tiles-components/panel1.jsp"/> <put name="body" value="CHANGE-ME"/> <put name="footer" value="/tiles-components/footer.jsp"/> </definition> <!-- ======================================================= --> <!-- Page definitions --> <!-- ======================================================= --> <!-- User Login page --> <definition name="tile.userLogin" extends="rootLayout"> <put name="titleString" value="User Login"/> <put name="body" value="/src/userLogin.jsp"/> </definition> <!-- User Main page --> <definition name="tile.userMain" extends="rootLayout"> <put name="titleString" value="User Main"/> <put name="body" value="/src/userMain.jsp"/> </definition> </tiles-definitions>
而 Nested 标记库的作用是让以上这些基本标记库能够嵌套使用,发挥更大的作用。
Commons Logging 接口
所谓的 Commons Logging 接口,是指将日志功能的使用与日志具体实现分开,通过配置文件来指定具体使用的日志实现。这样你就可以在 Struts 1.1 中通过统一的接口来使用日志功能,而不去管具体是利用的哪种日志实现,有点于类似 JDBC 的功能。Struts 1.1 中支持的日志实现包括:Log4J,JDK Logging API, LogKit,NoOpLog 和 SimpleLog。
你可以按照如下的方式来使用 Commons Logging 接口(可以参照 Struts 源文中的许多类实现):
package com.foo; // ... import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; //... public class Foo { // ... private static Log log = LogFactory.getLog(Foo.class); // ... public void setBar(Bar bar) { if (log.isTraceEnabled()) { log.trace("Setting bar to " + bar); } this.bar = bar; } // ... }
而开启日志功能最简单的办法就是在 WEB-INF/classes 目录下添加以下两个文件:
commons-logging.properties 文件:
# Note: The Tiles framework now uses the commons-logging package to output different information or debug statements. Please refer to this package documentation to enable it. The simplest way to enable logging is to create two files in WEB-INF/classes: # commons-logging.properties # org.apache.commons.logging.Log=org.apache.commons.logging.impl.SimpleLog # simplelog.properties # # Logging detail level, # # Must be one of ("trace", "debug", "info", "warn", "error", or "fatal"). #org.apache.commons.logging.simplelog.defaultlog=trace org.apache.commons.logging.Log=org.apache.commons.logging.impl.SimpleLog
simplelog.properties 文件:
# Logging detail level, # Must be one of ("trace", "debug", "info", "warn", "error", or "fatal"). org.apache.commons.logging.simplelog.defaultlog=fatal
这里我们采用的日志实现是 SimpleLog,你可以在 simplelog.properties 文件指定日志明细的级别:trace,debug,info,warn,error 和 fatal,从 trace 到 fatal 错误级别越来越高,同时输出的日志信息也越来越少。而这些级别是和 org.apache.commons.logging.log 接口中的方法一一对应的。这些级别是向后包含的,也就是前面的级别包含后面级别的信息。