Struts核心组件及 运行机制
Struts运行机制 Struts架构从本质上是MVC设计模式的具体实现 表示层一般使用视图组件实现、控制层使用控制器组件实现,而应用逻辑层则使用模型组件实现。作为表示层和应用逻辑层之间的中介,控制器处于视图及模型之间,起到了桥梁的沟通作用
对应的实现组件 与控制器组件相关的类主要包括: ActionServlet:Struts架构的中央控制器,用于接受浏览器客户端用户请求以及状态改变,并依据处理结果选择表示层不同视图的重定向。 RequestProcessor:包含了Struts控制器在处理servlet请求时所遵循的控制逻辑。 Action:控制器的一部分,用于模型交互,执行状态改变或状态查询,以及告诉ActionServlet 下一个选择的视图。 ActionForm:用于保存于表单数据相关的状态数据的改变。 ActionMapping:用于控制在状态数据改变后的事件处理的方式。 ActionForward:用户指向或者视图选择。 与视图组件相关的工具主要包括Struts架构中提供的强大标记库,常用的标记库功能如下所示: struts-html.tld标记库:扩展HTML Form的JSP标记 struts- bean.tld标记库:扩展扩展处理JavaBean的JSP标记 struts-logic.tld标记库:扩展测试属性值的JSP标记 与模型组件相关的工具主要包括: 开发者提供的其他数据服务和API,例如:JavaBean等。
控制器组件 Struts 架构中控制器组件所承担的主要功能包括: 在Struts框架中,整个控制工作是在一系列的相关组件的配合下共同完成的 接受浏览器客户端的处理请求 根据用户的不同请求,调用对应的模型组件来执行相应的业务逻辑 获取模型组件业务逻辑的处理结果 根据当前的状态数据及业务逻辑的处理结果,选择合适的视图组件呈现在浏览器客户端。 在Struts框架中,整个控制工作是在一系列的相关组件的配合下共同完成的
ActionServlet 在Struts架构中Controller组件的核心控制器组件是ActionServlet 是org.apache.struts.action.ActionServlet类型的servlet 所有客户端提交的用户请求都由它进行处理 负责接收客户端请求后将之分发到相应的Action Bean处理,再根据处理的结果将不同的显示请求重定向到响应页面。 实际上,ActionServlet完全是在幕后工作,它将其他组件绑定在一起。 该类继承自javax.servlet.http.HttpServlet,有标准的生命周期中所用到的方法.另外在ActionServlet类添加了一个特殊的process方法.process()方法的作用是处理接收的请求并作出相应 代码参见ActionServlet.java 第1184行 ActionServlet接收到Servlet容器的新的客户端请求后,它采取的具体的处理过程如下: 根据请求的种类执行相应的doGet或doPost方法,然后在doGet和doPost中调用process方法 Process方法首先会获得RequestProcessor类的对象,然后调用该类的process方法进行处理.实际上控制器所执行的控制逻辑就包含在RequestProcessor这个类中,而不是在ActionServlet中.ActionServlet处理业务的过程是借助RequestProcessor这个类的功能来实现的
RequestProcessor类 RequestProcessor类 一个应该模块对应一个RequestProcessor类; ActionSerlvet接收请求后调用RquestProcessor类的process方法,并把request和response传给它; 根据配置文件创建ActionMapping对象 RquestProcessor类的process方法从struts-config.xml文件中根据请求URI查询匹配的<action>子元素,然后根据<action>子元素中的“name”属性在配置文件中查找匹配的<form-bean>子元素,确定下一步要用到的ActionForm Bean类 RquestProcessor类的process方法调用ActionForm Bean类的setXXX方法,将表单中的数据填充到FormBean类的相应的属性中,然后根据<action>子元素中的validate属性的值,判断是否调用FormBean类的validate方法校验表单的数据 查找匹配的<action>子元素中的“type”属性寻找相应的可用的ActionBean类的对象,如果找不到就新创建一个 将FormBean类的对象,ActionMapping对象,request对象,reponse对象一起传给Action类的execute()方法 Action类的execute()方法执行完毕,返回一个表明相应页面的ActionForward对象. RequestProcessor类根据ActionForward对象进行调整
RequestProcessor类的process方法调用的方法列表 processMultipart()—预处理request的请求方式以及分析请求的contentType属性 processPath()—得到请求的url,分析后选择合适的Action组件; processLocale()—得到请求的locale,适当时候存放到session中; processContent()—通过ControllerConfig对象的contentType属性设置字符编码; processNoCache()--通过ControllerConfig对象的nocache属性设置页面缓存机制; processPreprocess()—这是一个空方法,直接返回true,一般用来扩展用的; processMapping()—匹配用户请求的URL只否有相应的ActionMapping对象; processRoles()—判断用户是否配置了安全角色; processActionForm()—查找ActionMapping对象邦定的ActionForm,并实例化; processPopulate()—如果找到ActionForm就把request中的表单值填到actionForm; ProcessValidate()—如果ActionMapping中validate属性为true.就调用表单的 validate方法进行数据验证; processForward()—调用requestDispatcher的forward()方法进行页面跳转; processInclude()--调用requestDispatcher的include()方法进行页面输出; processActionCreate()—创建一个action的实例存放到缓存中; processActionPerform()—调用action的execute()方法; precessActionForward()—根据action的execute()的返回值进行页面跳转;
扩展RequestProcessor 处理文字编码问题 我们可以去重写requestProcessor类的processPreprocess方法去处理编码; 并在struts-config.xml文件里面配置 <controller processorClass="struts.requestprocessor.MyRequestProcessor"> </controller>
例:MyRequestProcesser类 public class MyRequestProcessor extends RequestProcessor { protected boolean processPreprocess(HttpServletRequest request, HttpServletResponse response) { try { request.setCharacterEncoding("GBK"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return super.processPreprocess(request, response);
ActionForm org.apache.struts.action.ActionForm的子类的实例 用于保存用户请求表单中的数据,并可保持其状态的连续性,即在不同的页面间传递这些数据 由开发者定义的子类(ActionForm Bean类)中主要包含和用户表单数据同名的属性以及相应的存取方法(属性的get方法和set方法),而没有商业逻辑 此外,还可根据需要重写从父类继承来的reset()和validate()方法,实现属性重置和表单数据验证功能
处理ActionForm Bean的基本步骤 1)检查并确认在Action中已经配置了对表单Bean的映射。 2)根据表单Bean的name属性,查找表单Bean的配置信息。 3)检查该表单Bean的使用范围,查找在该范围内,是否已存在该Bean的实例。如果已经存在,那么就重用该实例。否则,就重新创建一个。 4)调用该Bean实例的reset()方法重置其状态。 5)调用该Bean实例相应的setter方法,使用请求表单中的数据填充Bean的属性。 6)如果起用了validate机制,则调用表单Bean的validate()方法。如果validate()方法返回任何错误,则跳过步骤7)转到预定的出错处理页面。 7)将该表单Bean实例作为参数,传给处理器对象的execute()方法并执行。
ActionForm的生命周期 接收控制器的请求 如果校验失败,将请求转发给<action> 的input属性所指定的web组件,并将 FormBean保存在指定的范围中 从request或session范围中取出 ActionForm对象,如果不存在就new一个 调用ActonForm对象的reset方法 如果校验成功调用Action的execute方法 并将FormBean传递给该方法 将FormBean对象保存在scope 指定的范围内 将请求转发给<action指定的web组件, 并将FormBean保存在指定的范围中 将用户的表单数据封装在FormBean中 如果<action>中的validate属性为true,调 用FormBean的validatte()方法
Action Bean <action-mappings> <action path="/regist" Action类是Struts架构中控制器组件的重要组成部分.它是用户请求和业务逻辑之间的媒介. org.apache.struts.action.Action子类类型, RequestProcessor调用它以处理不同的页面请求 它通过如下的方式发挥作用:开发者事先创建一个或多个Action的子类,在子类中加入所需要的处理逻辑,用于处理不同的业务逻辑.我们称这些子类为处理器类或ActionBean类 当Struts架构的核心控制器ActionServlet委托RequestProcessor对象处理一个用户的请求时, RequestProcessor对象除了使用FormBean来接受表单的数据外,还会查找配置文件中的<action-mapping>元素,根据请求页面的URI确定匹配的<action>子元素(参见下面的蓝色代码) RequestProcessor对象会针对当前用户请求寻找一个可用的Action类型的对象.如果找不到就创建一个新的对象,然后将客户端的请求发送给该处理器类的对象,并调用Action子类的execute方法进行处理. <action-mappings> <action path="/regist" type="test.RegistAction" name="formBean1" scope="session" /> <action path="/overview" forward="/links.jsp"/> </action-mappings>
ActionBean public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception mapping :是对struts-config.xml文件中配置信息的一个映射,它封装了当前处理器类对象(既ActionBean)的相关信息
ActionBean 设计ActionBean时注意事项 RequestProcessor对象对于每种ActionBean类型只创建一个实例.该实例将被用于所有匹配的请求,这就需要我们在定义ActionBean类时考虑如果在多线程的条件下使用.一个重要的原则就是在ActionBean类中尽量使用局部变量而不是实例变量 尽量在ActionBean类中加入异常处理逻辑,在方法内部捕获和处理可能出现的异常,而不是简单的抛出 在控制器将请求重新定向到其他页面前,释放ActionBean组件作占用的资源(如数据库连接) 要避免将所有的业务逻辑都直接写在ActionBean类中,这样会导致ActionBean类的可读性和可维护性变差.由于这些商业的业务逻辑代表被嵌入到只能运行在Web应用程序环境的组件中,很难做到代码重用 综上所述,ActionBean通常负责将客户端请求映射到处理这些请求的业务逻辑Bean,在转向生成响应回送到客户端解码,实际上起到Controll和Model之间的适配器作用,属于低层面的控制器
ActionMapping org.apache.struts.action.ActionMapping类型 当ActionServlet转发请求时,ActionMapping实例被作为参数之一传递给相应Action对象的execute()方法 ActionMapping对象映射的配置信息主要来自配置文件中的<action-mapping>和<global-forward>元素
ActionForward 一种配置对象,映射配置文件struts-config.xml中的<forward>元素,封装目标响应页面URI。 <forward> 元素不但可以定义在<global-forwards>元素中,作为全局的配置信息,也可以定义在<action>元素中,此时只能用于特定的Action处理. ActionForward的主要属性: name - 本ActionForward对象的标识,在Action Bean类 的execute()方法中,ActionMapping对象的 findForward()方法就是根据此标识来查找相应 的ActionForward对象的。 path - 目标响应页面URI redirect - 标明页面转向的方式 <global-forwards> <forward name="failed" path="/error.jsp"/> <forward name="successed" path="/right.jsp"/> </global-forwards>
模型组件 一般使用JavaBean来实现Model组件,对应着其作用可以分为两方面: 系统状态Bean(Application State) 业务逻辑Bean(Business Logic Bean) JavaBean的作用范围: page request session application
视图组件 视图组件中提交的请求被控制器组件接收.在实际的开发 中,在视图组件内可以提交请求的途径主要有两种 HTML表单:通过表单的action属性来指定 HTML超连接 <a href=“regist.do”>注册新用户 如果需要在发送请求的时候携带参数,可以按照如下 的方式 <a href=“regist.do”?name=aaa&password=aaa>注册 在视图组件中还可以使用Struts标记库
标记库初步 Struts在国际化的过程中会用到一个特殊的JSP标记<bean:message/>,用来获取属性文件中存放的字符串信息,在使用前,首先要部署包含此标记定义的Struts标记库,具体做法是: 将Struts发布包中的标记库文件struts-bean.tld复制到当前应用程序的WEB-INF下 在web.xml文件中添加相应的<taglib>子元素 <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd"> <web-app> <taglib> <taglib-uri>/WEB-INF/struts-bean.tld</taglib-uri> <taglib-location>/WEB-INF/struts-bean.tld</taglib-location> </taglib> </web-app> 在使用<bean:message/>标记的JSP文件中开头引入标记库文件 <%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %> 这样我们就可以在JSP中使用<bean:message>标记了
应用程序国际化 本地化:针对不同的语言版本环境的客户开发出不同语言环境的软件版本.如果需要对Web应用进行中文本地化操作,要注意处理中文乱码的问题:<%@ page pageEncoding="GBK" contentType="text/html; charset=GBK"%> 国际化:应用程序在运行时能够根据客户端请求所来自的国家/地区、语言的不同而显示不同的用户界面 internationalization,简称I18N。 资源包和属性文件:国际化能够提供多种语言界面,就应该在某个地方存放着所用到的各种语言的标签信息。这些字符串类型的信息事先已保存在多个文本文件中,每个文本文件对于一种不同语言的版本。这些文件我们称为属性文件,所有属性文件合作一起,我们称为资源包。 在struts-config.xml文件中添加<message-resources/>元素,用来指明所要用到的资源包 <struts-config> <message-resources parameter=“MyResource”> 属性文件中包含的是“键-值”对形式的字符串信息。该文件在程序启动时被载入内存,并根据客户端请求所携带的语言/地区信息进行匹配和使用,
应用程序国际化 属性文件的命名格式: 文件名前缀.properties 文件名前缀_语言种类.properties 种类是有效的ISO语言代码,ISO639标准定义的这些代码的 格式为:英文小写、双字符,见下表 缺省的属性文件 语言 编码 汉语(Chiness) zh 英语(Englist) en 法语(French) fr 德语(German) de 日语(Japanese) ja
案例 : 实现程序国际化 英文属性文件 MyResource.properties 案例 : 实现程序国际化 英文属性文件 MyResource.properties # Simple properties file for test. title.welcome = Welcome you! title.imageEnter=/enter.gif title.imgEnterHint=classmate title.imageRegist=/register.gif title.imgRegistHint=regist label.deny = Sorry,Login Failed! item.submit = submit item.reset = reset item.username = UserName item.password = Password item.password2 = Confirm Password item.regist = click here to regist item.loginAgain = login again item.registsubmit = regist item.back = go back to login
中文属性文件 MyResource_zh.properties # 中文属性文件 title.welcome = 欢迎欢迎 title.imageEnter=/enter.gif title.imgEnterHint=同学录 title.imageRegist=/register.gif title.imgRegistHint=注册 label.deny = 抱歉,登陆失败 item.submit = 提交 item.reset = 重制 item.username = 用户名 item.password = 密码 item.password2 = 确认 item.regist = 注册新用户 item.loginAgain = 重新登陆 item.registsubmit = 注册 item.back = 返回登陆
为了更好的解决中文问题,使用java提供的native2ascii命令来对中文属性文件进行编码.在cmd下执行该命令 格式如下: native2ascii -encoding gb2312 MyResource_zh.properties MyResource_zh_new.properties
重新编码后的中文属性文件格式如下 # \u4e2d\u6587\u5c5e\u6027\u6587\u4ef6 title.welcome = \u6b22\u8fce\u6b22\u8fce title.imageEnter=/enter.gif title.imgEnterHint=\u540c\u5b66\u5f55 title.imageRegist=/register.gif title.imgRegistHint=\u6ce8\u518c label.deny = \u62b1\u6b49,\u767b\u9646\u5931\u8d25 item.submit = \u63d0\u4ea4 item.reset = \u91cd\u5236 item.username = \u7528\u6237\u540d item.password = \u5bc6\u7801 item.password2 = \u786e\u8ba4 item.regist = \u6ce8\u518c\u65b0\u7528\u6237 item.loginAgain = \u91cd\u65b0\u767b\u9646 item.registsubmit = \u6ce8\u518c item.back = \u8fd4\u56de\u767b\u9646
Validate()方法 当Struts的配置文件满足以下两个条件,Struts控制器就会调用ActionForm的validate()方法; 为ActionForm配置了Action映射,即<form-bean>元素里面name和<action>元素里面的name相匹配; <action>元素的validate属性为true; 方法返回ActionErrors对象,如果该对象为空或不包含任何ActionMessage对象,那么就表示验证通过;反之把ActionErrors存到request中并返回input页面;
表单验证和错误处理 重写ActionForm的validate方法; 在validate方法里根据ActionForm中填充的属性值判断是否适合程序的要求; 返回一个ActionErrors对象; ActionErrors对象是一个MAP集合,存放的全都是ActionMessage对象; ActionMessage对象的错误提示配合Resource bundle文件使用; 如果ActionErrors集合里没有ActionMessage对象,则通过数据验证,执行Action的execute方法,否则返回ActionMapping对象用的input页面,并通过<html:errors>标签显示ActionMessage中的错误提示;
public ActionErrors validate(ActionMapping mapping, javax.servlet.http.HttpServletRequest request) { ActionErrors errors = new ActionErrors(); if(this.name.equals("") || this.name ==null){ errors.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage("error.name.required")); } if(this.psw.equals("") || this.psw ==null){ ActionMessage("error.psw.required")); return errors;
修改语言包 英文属性文件 error.name.required = <li><font color="red">UserName is not null</font></li> error.psw.required = <li><font color="red">Psw is not null</font></li><br> 中文属性文件 error.name.required = <li><font color="red"> \u7528\u6237\u540d\u4e0d\u80fd\u4e3a\u7a7a</font></li> error.psw.required = <li><font color="red"> \u5bc6\u7801\u4e0d\u80fd\u4e3a\u7a7a</font></li><br>
Reset()方法 不管ActionForm存在于那个scope,实例化之后ActionForm就会调用reset方法; Reset()方法用于恢复ActionForm的默认值,例如把boolean设置成true或false,把String设置为空;