1.validate()方法会校验action中所有与execute方法签名相同的方法;
2.要校验指定的方法通过重写validateXxx()方法实现, validateXxx()只会校验action中方法名为Xxx的方法。其中Xxx的第一个字母要大写;
3.当某个数据校验失败时,调用addFieldError()方法往系统的fieldErrors添加校验失败信息(为了使用addFieldError()方法,action可以继承ActionSupport),如果系统 的fieldErrors包含失败信息,struts2会将请求转发到名为input的result;
4.在input视图中可以通过<s:fielderror/>显示失败信息。
5.先执行validateXxxx()->validate()->如果出错了,会转发<result name=”input”/>所指定的页面,如果不出错,会直接进行Action::execute()方法.
1)拦截器是对调用的Action起作用,它提供了一种机制可以使开发者可以定义在一个action执行的前后执行的代码。拦截器只能拦截Action,说明白点拦截器其实是Action的功能块,只在Action前后执行。拦截器可以抽象出一部分代码可以用来完善原来的action。同时可以减轻代码冗余,提高重用率。
2) 过滤器是拦截用户请求的,范围明显比拦截器大的多。
1.在 struts2 中, 把请求参数映射到 action 属性的工作由 Params拦截器负责, 它是默认的 defaultStack 拦截器中的一员. Params拦截器可以自动完成字符串和基本数据类型之间转换.
2.在web应用中,提交的数据基本都是字符串。
3.对于引用类型的数据(除String、Collection)转换,需要自定义类型转换器;
4.自定义类型转化器必须实现TypeConverter 接口或对这个接口的某种具体实现做扩展,如:StrutsTypeConverter,重写convertToString和convertFromString方法;
局部:
创建一个属性文件: ActionClassName-conversion.properties, 该文件需和相对应的动作类(Action)放在同一个目录下, ActionClassName是Action的类名,后面的-conversion.properties 是固定写法。在properties文件中的内容为: 属性名称=类型转换器的全类名
全局:
在 WEB-INF/classes/ 目录下创建 xwork-conversion.properties 文件. 在properties文件中的内容为: 待转换的类型=类型转换器的全类名
注意:对于转换的是属性,只需要写属性名,如果是对应的是类型,则需要写全类名 。
第一步:在表单中加入<s:token />
第二步,使用token栏截器,定义invalid.token结果集 ,要使用<s:token /> 必须要在struts.xml的action中引用token的预定义拦截器。
<interceptor name="token" class="org.apache.struts2.interceptor.TokenInterceptor"/>
当然还可以使用tokenSession
<interceptor name="tokenSession"
class="org.apache.struts2.interceptor.TokenSessionStoreInterceptor"/>
一、造成重复提交主要的两个原因:
在平时的开发过程中,经常可以遇到表单重复提交的问题,如做一个注册页面,如果表单重复提交,那么一个用户就会注册多次,重复提交主要由于两种原因。
1、 一是,服务器处理时间久。当用户在表单中填完信息,点击“提交”按钮后,由于服务器反应时间过长没能及时看到响应信息,或者出于其它目的,再次点击“提 交”按钮,从而导致在服务器端接收到两条或多条相同的信息。如果信息需要存储到后台数据库中,如此以来就会产生数据库操作异常提示信息,以至于给用户带来 错误信息提示,从而给用户的使用带来不便。
2、二是,forward跳转引起的重复提交。在页面跳转的时候,有两种类型:请求转发和重定向,所谓请求转发是在服务器端进行跳转,对用户是透明的,此时浏览器中的地址不会发生改变,重定向是在客户端发生跳转,跳转时候浏览器中的地址栏会发生改变,如果我们在注册时,使用了请求转发,那么当我们刷新页面时,就会引起表单的重复提交
二、解决方案
方案一、使用struts2中的token拦截器或者tokenSession拦截器
对token的简单理解:
1)当用户首次访问包含表单的页面时,服务器会在这次会话中创建一个session对象,并产生一个令牌值,然后将这个令牌值作为隐藏输入域的值,随表单一起发送到服务器端,同时将令牌值保存到Session中。
2) 当用户提交页面时,服务器首先判断请求参数中的令牌值和Session中保存的令牌值是否相等,若相等,则清楚Session中的令牌值,然后执行数据处 理操作。如果不相等,则提示用户已经提交过了表单,同时产生一个新的令牌值,保存到Session中。当用户重新访问提交数据页面时,将新产生的令牌值作 为隐藏输入域的值。
token: 在活动Action中检查合法令牌(token), 防止表单的重复提交;
token-session: 同上, 但是在接到非法令牌时将提交的数据保存在session中;
主要步骤如下:
第一步:在表单中加入<s:token />(当然啦!要首先导入struts2的标签库 <%@taglib uri=”/struts-tags” prefix=”s” %>)
<s:form action="helloworld_other" method="post" namespace="/test">
<s:textfield name="person.name"/><s:token/><s:submit/>
</s:form>
第二步:在struts.xml配置文件中相应的action上配置token拦截器或者tokenSession拦截器。此拦截器只能用在有form的提交请求上。
<action name="helloworld_*" class="com.jim.action.HelloWorldAction" method="{1}">
<interceptor-ref name="defaultStack"/>
<interceptor-ref name="token" />
<result name="invalid.token">/WEB-INF/page/message.jsp</result>
<result>/WEB-INF/page/result.jsp</result>
</action>
以上配置加入了“token”拦截器和“invalid.token”结果,因为“token”拦截器在会话的token与请求的token不一致时,将会直接返回“invalid.token”结果。
方案二:基于第二种重复提交表单的原因,服务器内部使用重定向
在Struts2中的struts.xml中,result标签的type默认值是 dispatcher,(请求转发),要实现以上的功能,肯定不能使用默认值,我们需要将其值设为redirectAction(重定向到某一 Action),也就是说重一个Action跳转到另外一个Action,此时浏览器中的地址是第二个Action,结合到上面的需求,我们可以再第一个 Action中将记录添加到数据库中,然后在第二个Action中将数据读取出来。以后每次刷新,那么只会执行第二个Action。
到struts-default.xml中找到与redirectionAction对应的一个类,ServletActionRedirectResult
从Api文档中发现找出里面有一个字段叫做actionName,指定我们需要跳转的Action,配置方法如下:
<action name="add" class="com.action.AddAction">
<result name="success" type="redirectAction">
<param name="actionName">show_show</param>
</result>
</action>
1、JSP页面:
JSP页面的上传文件的组件:<s:file name=”upload” />,如果需要一次上传多个文件, 就必须使用多个 file 标签, 但它们的名字必须是相同的,即:name=“xxx”的值必须一样;
2. 必须把表单的enctype属性设置为:multipart/form-data;
3.表单的方法必须为post,因为post提交的数据在消息体中,而无大小限制。
对应的action:
4.在 Action 中新添加 3 个和文件上传相关的属性
5.如果是上传单个文件, uploadImage属性的类型就是 java.io.File, 它代表被上传的文件, 第二个和第三个属性的类型是 String, 它们分别代表上传文件的文件名和文件类型,定义方式是分别是:jsp页面file组件的名称+ContentType, jsp页面file组件的名称+FileName
6如果上上传多个文件, 可以使用数组或 List
1. 基于 MVC 架构,框架结构清晰。
2. 使用 OGNL: OGNL 可以快捷的访问值栈中的数据、调用值栈中对象的方法
3. 拦截器: Struts2 的拦截器是一个 Action 级别的 AOP, Struts2 中的许多特性都是通过拦截器来实现的, 例如异常处理,文件上传,验证等。拦截器是可配置与重用的
4. 多种表现层技术. 如:JSP、FreeMarker、Velocity 等
1. 与 Servlet API 解耦的访问方式,通过 ActionContext 访问域对象对应的 Map 对象,通过实现 Aware 接口使 Struts2 注入对应的 Map 对象
2. 与 Servlet API 耦合的访问方式,通过 ServletActionContext 直接获取 Servlet API 对象,通过实现 ServletXxxAware 接口的方式使 Struts2 注入对应的对象
每个拦截器都是需要实现 Interceptor 接口
init():在拦截器被创建后立即被调用, 它在拦截器的生命周期内只被调用一次. 可以在该方法中对相关资源进行必要的初始化;
intercept(ActionInvocation invocation):每拦截一个动作请求,该方法就会被调用一次;
destroy:该方法将在拦截器被销毁之前被调用, 它在拦截器的生命周期内也只被调用一次;
1. JSON plugin
2. DOJO plugin
3. DWR plugin
4. 使用 Stream 结果类型.
1、需要在struts配置文件中指定资源属性文件的位置和名称,如
2、在相应的位置放置相应的文件
3、在JSP页面中使用来输出文本,以避免硬编码
以登录页面的国际化作为例子讲解
创建相应的资源属性文件
用标签替换登录页面的硬编码文本
测试(更改网页显示语言,以便测试不同的版本)
编程式异常处理
即我们在Action中调用业务逻辑层对象的方法时,用try{ }catch的方式来截获异常之后,手工对异常进行处理
我们以前的开发过程中,都是使用编程式的异常处理
在编程式异常处理的时候,我们可以使用struts的消息处理机制(前面所讲的内容)来对这些异常信息进行处理
自动异常处理机制
即在Action中不捕捉异常,而是将异常抛出给struts框架处理
我们需要在配置文件中指示struts如何处理这些被抛出的异常
使用元素来定义自动异常处理
元素的配置,指示了struts如何处理异常的方式
在通常的情况下,我们得到异常以后,需要将页面导航到一个错误提示的页面,提示错误信息
元素配置的关键属性是:
key – 即这个异常所对应的错误提示消息文本的key,这个key的值,需要在资源属性文件中进行定义
type – 即定义需要处理哪种类型的Exception
path – 定义一旦出现异常,需要转向哪个页面来进行提示,如果不定义path属性,默认情况下,将使用 Action配置中的input属性的值来作为转向的页面
path属性:指定请求访问Action的路径
type属性:指定Action的完整类名
name属性:指定需要传递给Action的ActionForm Bean
scope属性:指定ActionForm Bean的存放范围
validate属性:指定是否执行表单验证
input属性:指定当表单验证失败时的转发路径。
<action>元素还包含一个<forward>子元素,它定义了一个请求转发路径。
1、ActionForm Bean也是一种JavaBean,除了具有一些JavaBean的常规方法,还包含一些特殊的方法,用于验证HTML表单数据以及将其属性重新设置为默认值。
2、Struts框架利用ActionForm Bean来进行View组件和Controller组件之间表单数据的传递。
3、Struts框架把View组件接受到的用户输入的表单数据保存在ActionForm Bean中,把它传递给Controller组件,Controller组件可以对ActionForm Bean中的数据进行修改JSP文件使用Struts标签读取修改后的ActionForm Bean的信息,重新设置HTML表单。
(1)检查Action的映射,确定Action中已经配置了对ActionForm的映射
(2)根据name属性,查找form bean的配置信息
(3)检查Action的formbean的使用范围,确定在此范围下,是否已经有此form bean的实例。
(4)假如当前范围下,已经存在了此form bean的实例,而是对当前请求来说,是同一种类型的话,那么就重用。
(5)否则,就重新构建一个form bean的实例
(6)form bean的reset()方法备调用
(7)调用对应的setter方法,对状态属性赋值
(8)如果validatede的属性被设置为true,那么就调用form bean的validate()方法。
(9)如果validate()方法没有返回任何错误,控制器将ActionForm作为参数,传给Action实例的execute()方法并执行。
答:FormBean是一种JAVABean,除了具有JAVABean的常规方法,还包含一些特殊方法,用于验证表单数据,以及将其属性重新设置为默认值。 FormBean用来进行View组件和Controller组件之间表单数据的传递。View组件接收到用户输入的表单数据,保存在FormBean中,把它传递给Controller组件,Controller组件可以对FormBean中的数据进行修改。
答:Forward是根据Action return的值找到对应的JSP页。当多个Action共同return同一个值时,可将这个Forward元素写在Global-Forward中。
实现Action 接口
使用Struts2 @Action 元注解
继承ActionSupport类必须实现 execute() 方法,返回一个可配置的字符串
Struts2的Action类是线程安全的,因为每个请求一个实例。
Struts2的拦截器是单例,所以它不是线程安全的,我们需要仔细地实现它们,以避免与共享数据的任何问题。
过滤器decorator模式和职责链模式
组件 | Struts1 | Struts2 |
---|---|---|
Action 类 | 需要强迫继承一个类,不灵活 | 只要实现Action接口, 完成execute() |
线程安全 | Action类是单例的,非线程安全,编程时要注意多线程副作用 | action类每次请求一个(性能降低) |
Servlet | Struts1 API 与Servlet API 紧紧耦合,Request和Response 对象直接传入execute() 方法(虽然很方便) | Struts2 API 和Servlet API松耦合,自动将表单bean数据映射到action类的javabean属性(struts1也可以通过ActionForm实现) |
测试 | Struts1 action类因为和Servlet API 耦合难于测试 | Struts2 Action 类是一个正常Java类,易于测试 |
Request 参数映射 | Struts1 需要我们创建 ActionForm 类来 hold request 参数,还需要配置 | Struts2 request参数映射是自动的,我们只要有相应的java bean属性在action类中,或实现 ModelDriven接口提供java bean 类名用于映射 |
标签支持 | Struts1 使用 JSTL 和自己的表达式 | Struts2 支持手动和验证框架整合 |
视图输出 | Struts1 使用标准JSP 输出 | Struts2 使用 ValueStack 存储请求参数和属性,使用OGNL 和 Struts2 标签访问 |
org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter 每次请求处理从这里开始。
ValueStack是一个用来处理客户端请求的数据存储区,数据存储在ActionContext中,这是用ThreadLocal为每个请求线程创建的,生命周期是请求级别。
对象图导航语言Object-Graph Navigation Language (OGNL) 是用户将储存在valuestack中的数据导出,使用OGNL拦截器和结果页面都能访问到ValueStack的数据。
@Action创建action
@Actions 配置一个类为多个Action
@Namespace 和 @Namespaces 用于创建不同的模块
@Result 用于结果页面
@ResultPath用于配置结果页面的定位。
struts.devMode 是运行在开发调试模式,提供日志和调试功能,提交生产环境需要关闭。
struts.convention.result.path 配置结果页面的位置,缺省Struts2在 {WEBAPP-ROOT}/{Namespace}/下寻找结果页面, 用这个常量能够改变这个位置。
struts.custom.i18n.resources 用来定义 i18n 支持.
struts.action.extension 用来配置应用程序的URL后缀. 缺省是后缀 .action。
<constant name="struts.devMode" value="true"></constant>
<constant name="struts.action.extension" value="action,do"></constant>
<constant name="struts.custom.i18n.resources" value="global"></constant>
<constant name="struts.convention.result.path" value="/"></constant>
我们能使用名称空间基于功能分离我们的Action类,如admin user和客户等模块。
com.opensymphony.xwork2.interceptor.ParametersInterceptor 配置在struts-default包中,名称是”params”。这是basicStack和 defaultStack一部分。
1. org.apache.struts.action
基本上,控制整个struts framework的运行的核心类、组件都在这个包中,比如我们上面提到的控制器ActionServlet。已经Action,ActionForm,ActionMapping等等。struts1.1比1.0多了 DynaActionForm 类。增加了动态扩展生成FormBean功能
2. org.apache.struts.actions
这个包是主要作用是提供客户的http请求和业务逻辑处理之间的特定适配器转换功能,而1.0版本中的部分动态增删FromBean的类,也在struts1.1中被Action包的DynaActionForm组件所取代
3. org.apache.struts.config
提供对配置文件struts-config.xml元素的映射。这也是sturts1.1中新增的功能
4. org.apache.struts.util
Strtuts为了更好支持web application的应用,提供了一个些常用服务的支持,比如Connection Pool和Message Source。详细信息请参考
5. org.apache.struts.taglib
这不是一个包,而是是一个客户标签类的集合。下面包括Bean Tags,HTML Tags,Logic Tags,Nested Tags,Template Tags这几个用于构建用户界面的标签类。
6. org.apache.struts.validator
Struts1.1 framework中增加了validator framework,用于动态的配置from表单的验证
ActionServlet继承自javax.servlet.http.HttpServlet类,其在Struts framework中扮演的角色是中心控制器。它提供一个中心位置来处理全部的终端请求。控制器ActionServlet主要负责将HTTP的客户请求信息组装后,根据配置文件的指定描述,转发到适当的处理器。
按照Servelt的标准,所有得Servlet必须在web配置文件(web.xml)声明。同样,ActoinServlet必须在Web Application配置文件(web.xml)中描述,有关配置信息如下。
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
</servlet>
全部的请求URI以*.do的模式存在并映射到这个servlet,其配置如下:
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
一个该模式的请求URI符合如下格式:
http://www.my_site_name.com/mycontext/actionName.do
中心控制器为所有的表示层请求提供了一个集中的访问点。这个控制器提供的抽象概念减轻了开发者建立公共应用系统服务的困难,如管理视图、会话及表单数据。它也提供一个通用机制如错误及异常处理,导航,国际化,数据验证,数据转换等。
当用户向服务器端提交请求的时候,实际上信息是首先发送到控制器ActionServlet,一旦控制器获得了请求,其就会将请求信息传交给一些辅助类(help classes)处理。这些辅助类知道如何去处理与请求信息所对应的业务操作。在Struts中,这个辅助类就是org.apache.struts.action.Action。通常开发者需要自己继承Aciton类,从而实现自己的Action实例。
ActionServlet把全部提交的请求都被控制器委托到RequestProcessor对象。RequestProcessor使用struts-config.xml文件检查请求URI找到动作Action标示符。
一个Action 类的角色,就像客户请求动作和业务逻辑处理之间的一个适配器(Adaptor),其功能就是将请求与业务逻辑分开。这样的分离,使得客户请求和Action类之间可以有多个点对点的映射。而且Action类通常还提供了其它的辅助功能,比如:认证(authorization)、日志(logging)和数据验证(validation)。
Action最为常用的是execute()方法当Controller收到客户的请求的时候,在将请求转移到一个Action实例时,如果这个实例不存在,控制器会首先创建,然后会调用这个Action实例的execute()方法。Struts Framework为应用系统中的每一个Action类只创建一个实例。因为所有的用户都使用这一个实例,所以你必须确定你的Action 类运行在一个多线程的环境中。
1) 客户端初始化一个指向Servlet容器(例如Tomcat)的请求
2 )这个请求经过一系列的过滤器(Filter)(这些过滤器中有一个叫做ActionContextCleanUp的可选过滤器,这个过滤器对于Struts2和其他框架的集成很有帮助,例如:SiteMesh Plugin)
3 )接着FilterDispatcher被调用,FilterDispatcher询问ActionMapper来决定这个请是否需要调用某个Action
4 )如果ActionMapper决定需要调用某个Action,FilterDispatcher把请求的处理交给ActionProxy
5 )ActionProxy通过Configuration Manager询问框架的配置文件,找到需要调用的Action类
6 )ActionProxy创建一个ActionInvocation的实例。
7 )ActionInvocation实例使用命名模式来调用,在调用Action的过程前后,涉及到相关拦截器(Intercepter)的调用。
8 )一旦Action执行完毕,ActionInvocation负责根据struts.xml中的配置找到对应的返回结果。返回结果通常是(但不总是,也可 能是另外的一个Action链)一个需要被表示的JSP或者FreeMarker的模版。在表示的过程中可以使用Struts2 框架中继承的标签。在这个过程中需要涉及到ActionMapper
在JSP页面中,使用标签,即可将其异常对应的错误消息文本进行显示(测试login.jsp页面)