Struts2

Struts2是在WebWork2基础发展而来的,和struts1一样,Struts2也是属于MVC框架。
不顾有一点大家需要注意的是:尽管Struts2和struts1名字上差别不是很大,但是
Struts2和Struts1在代码编写风格上几乎是不一样的。那么既然有了struts1,为何
还需要推出Struts2呢?

主要是因为Struts2有以下优点:
1.在软件设计上Struts2没有像struts1那样跟ServletAPI和StrutsAPI有着紧密的耦合。
Struts2的应用可以不依赖ServletAPI和SturtsAPI。Struts2的这种设计属于无侵入式
设计,而strut1却属于侵入式设计
public class OrderListAction extends Action{
public ActionForward ectcute(ActionMapping mapping ,ActionForm form,
HeetServletRequest request, HttpServletResponse response)
throws Exception{

}
}
2.Struts2提供了拦截器,利用拦截器可以进行AOP编程,实现如权限拦截等功能

3.Struts2提供了类型转换器,我们可以把特殊的请求参数转换成需要的类型。
在struts1中,如果我们需要实现同样的功能,就必须向Struts1的底层实现BeanUtil
注册类型转换器才行。

4.Struts2提供支持多种表现层技术。如JSP、freeMarker、Velocity。

5.Struts2的输入校验可以对指定方法进行校验,解决了Strut1长久之痛。

6.提供了全局范围,包范围和Action范围的国际化资源文件管理实现。

搭建Struts2开发环境
搭建Struts2环境是,我们一般需要做一下几个步骤的工作:
1.找到开发Struts2应用需要使用到的jar文件。
2.编写Struts2的配置文件 struts.xml
3.在web.xml中加入Struts2 MVC框架启动配置

搭建Strut2开发环境 --开发Struts2应用依赖的jar文件

大家可以到。。。。。下载 struts-----all.zip。下载完后解压文件,开发struts2
应用需要的jar文件在解压目录lib文件写下。不同的应用需要的JAR包是不同的。
下面给出了开发Struts2程序最小需要的JAR包。

struts2-core---.jar Struts2框架的核心类库
xwork-----.jar XWORK类库,Struts2在其上构建 
ognl------.jar 对象图导航语言(Object Graph Navigation Language),struts2框架通过其读写对象的属性
freemarker--.jar struts2的UI标签的模板,使用FreeMaker编写
commons-logging---.jar ASF出品的日至宝,Strut2框架使用这个日至宝来支持Log4j和JDK1.4+的日志记录
commons-fileupload--.jar 文件上传组件


搭建Struts2开发环境---Struts2应用的配置文件

Struts2默认的配置为struts.xml,该文件需要存放在WEB-INF/classes下,该文件
配置模板如下:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC 
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/strut-2.0.dtd" >
<struts>
</struts>

搭建Struts2开发环境---Struts2在web中的启动配置
在struts1.X中,struts框架是通过Servlet启动。
在Struts2中,strut框架是通过Filter启动的,在web.xml中的配置如下:
<filter>
<filter-name>struts2</filter>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepaerAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/</url-pattern>
</filter-mapping>

在SturtsPrepareAndExecuteFilter的init()方法中将会地区类路径下默认的配置文件
struts.xml完成初始化的操作。

Strut2读取到struts.xml的内容后,以javabean形式存放在内存中,以后struts2
对客户端的每次请求处理将使用内存中的数据,而不是每次都读取struts.xml文件的内容

Struts.xml 配置中的包介绍
<package name="soldier" namespace="/test" extends="struts-default">
<action name="hello" class="cn.soldier.acion.HelloAction" method="execute">
<result name="success">/WEB-INF/page/hello.jap</result>
</action>
</package>

在struts2框架中使用包来管理action,包的作用和java中的雷暴是非常相似的。
它主要用于管理同一组业务功能相关的action,在实际应用中,我们应该把一组
业务功能相关的action放在同一个包下。

配置包是必须指定name属性,该那么属性值可以任意取名,但必须唯一,他不对应
java的类包,如果其他包要继承该包,必须通过属性进行引用。
包的namespace属性用于定义该包的命名空间,命名空间作为访问包下action的路径的一部分,
如访问上面例子的,访问路径为/test/hello.action.
如果namespace不配置或配置为"" ,则该package为默认的命名空间。

通常每个包都应该继承 struts-default包,因为struts2很多核心功能都是拦截器
实现的,如:从请求中把请求参数封装到action,文件上传和数据验证等等,都是
通过拦截器实现的。struts-defaute定义了这些拦截器和Result类型。可以这么说
当包继承了struts-default才能使用struts提供的核心功能。

struts-default包实在struts2-core-xxx.jar文件中的struts-default.xml中定义。
struts-default.xml也是struts2默认配置文件。
Struts2每次都自动加载struts-default.xml文件。

包还可以通过abstract=true定义为抽象包,抽象包总不能包含action

 

Acrion名称的搜索顺序
1.获得请求路径的url,例如url是:http://server/struts2/path1/path2/path3/test.action

2.首先寻找namespace为/path1/path2/path3的package,
如果不存在这个package则执行步骤3,如果存在这个package,则在这个package中寻找名字为test的action,
当在该package下寻找不到action时,就会直接跑到默认namespace的package里寻找action(默认的命名空间为空字符串""),
如果在默认namespace的package里面还寻找不到该action,则页面提示找不到action。

3.寻找namespace为/path1/path2的package,如果不存在这package,则转至步骤4,如果存在这个package
则在这个package中寻找名字为test的action,当在该package中寻找不到action是就会直接跑到
默认的namespace的package中去找名字为test的atcion,在默认的namespace的package里还寻找不到该action
则页面提示找不到action。

4. 寻找namespace为/path1的package,如果不存在这个package则执行步骤5,如果存在这个package,
则在这个package中寻找名字为test的action。当装在该package中寻找不到action是就会直接跑到默认的namespace
的package中去找名字为test的action,在默认的namespace的package里还寻找不到该action则页面体制找不到
该action。

5.寻找namespace的package,如果存在这个package,则早这个package中寻找名字为test的action,当在package中
找不到action或不存在这个package是,都会去默认namespace的package里面寻找action,如果还是找不到则
页面显示找不到action。

action配置中的各项默认值
<package name="soldier" namespace="/test" exetends="struts-default">
<action name="helloworld" class="cn.soldier.HelloworldAction" method="extcute" >
<result name="success">/WIN-INF/page/hello.jsp</result>
</action>
<action name="addUI">
<result>/WIN-INF/page/hello.jsp</result>
</action>
</package>

1.如果没有为action指定class,默认是ActionSupport
2.如果没有为action指定method。默认执行action中的execute方法
3.如果没有指定result的name属性,默认值为success

action中result的各种转发类型
<action name="hello" cless="cn.soldier.HelloAction" method="extcute">
<result name="success">/WEB-INF/page/hello.jsp</result>
</action>

result配置类似于struts1中的forward,struts2中提供了多种结果类型,常用的类型有有
dispatcher(默认值)、redirect、redirectAction、plainText

在result中还可以使用{$属性名}表达式访问action中的属性,表达式的属性名对应action中的属性。
例子如下:
<result name="success" type="redirect">/WEB-INF/page/hello.jsp?id=${id}</result>

下面是redirectAction结果类型的例子,
如果重定向的action在同一个包下
<result name="success" type="redirect">hello</result>
如果重定向的action不在同一个包下
<result type="redirectAction">
<param name="actionName">action名</param>
<param name="namespace">namespace名</param>
</result>

plantext显示原始文件内容,
例如:当我们需要原样显示jsp文件源码的时候,我们可以用这个参数
<result name="soutce" type="plantext">
<param name="location">/xxx.jsp<param>
<param name="charSet">UTF-8</param><!--指定读取文件的编码-->

 

指定需要Struts2处理的请求后缀
前面我们都是默认使用.action后缀访问action。其实默认后缀是可以通过
常量"struts.action.extesion"进行修改的。如下我们配置Struts2只处理
以.do为后缀的请求路径:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.ort/dtds/struts-2.0.dtd">
<struts>
<constan name="stuts.action.extension" calue="do" />
</struts>
如果用户需要指定多个请求后缀,则多个后缀之间以英文逗号隔开,如
<constant name="struts.action.extension" value="do,go">

细说常量定义

常量可以在struts.xml或struts.properties中配置,建议在struts.xml中配置
两种配置方式如下

在stratus.xml文件中配置常量
<struts>
<constant name="stratus.action.extension" value="do" />
<struts/>

在stratus.properties中配置常量
stratus.action.extension=do

因为常量可以在多个配置文件中定义,所以我们需要里了解struts2加载常量的
搜索顺序
struts.xml
struts-piugin.xml
struts.xml
struts.properties
web.xml
如果在多个文件中配置同一个常量,则后一个文件中配置的常量值会覆盖
前面文件中配置的常量值。


常用的常量介绍
<!--指定默认编码集。作用于HttpServletRequest的setCharracterEncoding方法
和freemaker、velocity的输出-->
<constant name="struts.i18n.enconding" value="UTF-8" />
<!--指定需要Struts2处理的请求后缀,该属性的默认值是action。即所有匹配
".acion"的请求都由struts2处理,如果用户需要指定多个请求后缀,则多个后缀
之间以英文逗号隔开
<constant name="struts.action.extension="do" />

<!--设置浏览器是否缓存静态内容默认值为true(生产化境下使用),开发阶段最好关闭 -->
<constant name="strut.serve.static.browCache" value="false" />

<!--当struts配置文件修改后,系统是否自动重新加载该文件 默认值为false(生产环境
下使用),开发阶段最好打开 -->
<constant name="struts.configuration.xml.reload" value="true" />

<!--开发模式下使用,这样可以打印出更详细的错误信息-->
<constant name="struts.devMode" value="true" />

<!--与spring集成时,指定由sping负责action对象的创建-->
<constant name="struts.objectFactory" value="spring" />

<!--默认视图 -->
<constant name="struts.ui.theme" value="simple" />

<!--该属性设置Struts2是否支持动态啊方法调用,该属性的默认值是true。
如果需要关闭动态方法调用,则可以设置该属性为false-->
<constant name="struts.enable.DynameicMethodInvocation" vanle="false">

<!--上传文件的大小限制-->
<constant name="struts.multipart.maxSize" valie="107010196">

用户请求-->StrutsPrepareAndExecuteFilter
-->Interceptor struts内置的一些拦截器或用户自定义拦截器
-->Action 用户编写action类,类似struts中的action
-->Result 类似struts1中的forward 
-->Jap/html
响应<--

StrutsPrepareAndExecuteFilter是struts2框架的核心控制器,它负责拦截
有<url-pattern>/<url-pattern>指定的所有用户请求,当用户请求到达时,
该Filter会过滤用户的请求,默认情况下,如果用户请求不带后缀或者后缀
以.action结尾,这时请求将被转入struts2框架处理,否则Struts2框架将忽
略过该请求的处理。当请求转入struts2框架时会先经过一系列的拦截器,
然后再到Action。与Strut1不同,Struts2对用户的每一请求都会创建一个
Action,所以Struts2中的Action是线程安全的。


为应用指定多个struts配置文件

在大部分应用里,随着应用规模的增加,系统中Action的数量也会大量增加
导致struts.xml配置文件变得非常臃肿,为了避免struts.xml文件过于庞大、
臃肿,提高struts.xml问价你的可阅读性,我们可以将struts.xml配置文件
分解为多个配置文件,然后再struts.xml文件中包含其他配置文件、
夏敏的struts.xml文件通过<include>元素指定多个配置文件:

<!--struts.xml-->
<?xml version"1.0" encoding=""UTF-8 ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts/apache/org/dtds/struts-2.0.dtd">
<struts>
<include file="struts-user.xml" />
<include file="struts-order.xml" />
</struts>
/*********************************************************/
<!--struts-user.xml-->
<?xml version"1.0" encoding=""UTF-8 ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts/apache/org/dtds/struts-2.0.dtd">
<struts>

</struts>
/*********************************************************/
<!--struts-order.xml-->
<?xml version"1.0" encoding=""UTF-8 ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts/apache/org/dtds/struts-2.0.dtd">
<struts>

</struts>

通过这种方式,我们就可以将struts2的Action按模块添加在多个配置文件中。


动态方法调用
如果Action中存在多个方法是,我们可以使用!+方法名调用指定的方法。
例如:
public class HelloWorldAction{
private String message;

public String ececute()throws Exception{
this.meaage= "呵呵呵";
return "success";
}

public String other()throews Exception{
this.message="第二个方法";
return "success";
}
}

假设访问上面的action的URL路径为: /struts/test/helloword.action
要想访问action的other()方法,我们可以这样调用:
/struts/test/hellowoild!other.action //关键字 !other !方法名
<!--如果不想只用动态方法调用,可以设置常量关闭动态方法 -->
<constant name="struts.enable.DynameicMethodInvocation" value="flase" />.

使用通配符定义action
<package name="soldier" namespace="/test" extends="struts-default">
<action name="hello_*" class="cn.soldier.action.HelloAction" method="{1}">
<result name="success">/WEB-INF/page/hello.jsp</result>
</action>
</package>

public class HelloAction{
private String message;

public String execute() throws Exception{
this.message="呵呵";
return "success";
}

public String other() throws Exception{
this.message="第二个方法";
return "success";
}
}
//要想访问execute() 访问url: /test/hello.atcion
//要想访问other() 访问url: /test/hello!other.action

接收请求参数

采用基本类型接收请求参数(get/post)
在action类中定义与请求参数同名的属性,struts2便能自动接收请求参数并
赋予给同名属性。
例如:
请求路径为:http://locathost:8080/test/view.action?id=12
public class ProductAction{
private Integer id;
//struts2通过反射技术调用与请求参数同名的属性的setter方法或获取访问请求参数值
public void setId(Integer id){ this.id = id;}
public Integer(){return id}
}

采用复合类型接收参数
请求路径: http://locathost:8080/test/view.action?pruduct.id=78
public class ProductAction{
private Product prodect;

public void setProdect(Prodect product){
this.product = product;
}

public Produce getProduct(){return product;}
}
Struts2首先通过反射技术调用Product的默认构造器创建product对象,然后再通过
反射技术调用product中与请求参数同名的属性setter方法获取请求参数值。

自定义了类型换器的步骤
1.继承 DefaultTypeConverter 类
2.重写 converterValue 方法

局部类型转换器


public DateConverter extends DefaultTypeConverter{
@Override
public Object converterValue(Map context,Object vlaue,Class toType){
try{
//当字符串向Data类型转换时
if(toType == Date.class){
String[] params = (String[])value;
return dateFomat.parse(params[0]);
}else if(totype == String.class){
Data data = (object) value;
return deteFormat.format(date);
}
}catch(ParseException e){
return null;
}

}

注册局部类型转化器
转换器写好之后需要注册才能使用,可以注册全局的类型转换器和局部类型转换器

将类型转换器注册为局部类型转化器:
1.在Action类所在的包下放置ActionClassName-conversion.properties文件
ActionClassName是Action的类名,后面的-conversion是固定的写法,
2.在properties文件中的内容为:属性名=类型转化器的全类名


对于上例而言,文件的名称应该为
HelloWorldAction-conversion.properties

HelloWorldAction-conversion.properties文件中的内容为:
createtime=cn.soldier.conversion.DateConverter

自定义全局类型转换器

1.在WEB-INF/classes下放置xwork-conversion.properties文件
2.在properites文件中的内容为:待转换的类型=类型转换器的全类名

将上面类型注册为全局类型转换器:
xwork-conversion.properties文件中的内容为
java.util.Data = cn.soldier.conversion.DataConverter

访问或添加request、session、application属性
public String scope() throws Exceprion{
ActionContext ctx = ActionContext.getContext();
ctc.getAplictaion.put("app","应用范围")//往ServletContext里嵌入app
ctx.getSession()put("ses","session范围");//往session里放入ses
ctx.put("req","request范围")//往request里放入req
return "scope";
}

JSP:
<body>
$(applicationScope.app);
$(sessionScope.ses);
$(requestScope.req)
</body>


获取HttpServletRequest、HttpSession、ServletContext、HttpServletResponse对象
方法一:通过ServletActionContext类直接获取:
public String rsal() throws Exception{
HttpServletRequest request = 
ServletActionContext.getRequest();

ServletContext servletContext =
ServletActionContext.getServletContext();

HttpSession session = request.getSession();

HttpServletResponse response = 
ServletActionContext.getResponse();

return "rsal";
}
方法二:通过实现特定的接口实现 XXXAware

public class HelloWorldAction imlpements ServletRequestAware,
ServletResponseAware,ServletContextAware{
private HttpServletRequest request;
private ServletContext servletContext;
private HttpServletResponse response;

public void setServletRequest(HttpServletRequest q){
this.request = q;
}

public void setServletResponse(HttpServletResponse res){
this.response = res;
}

public void setServletContext(ServletContex ser){
this.servletContext = ser;s
}

}


使用el表达式
1.添加jar文件 jstl.jar(version 1.1) standard.jar 
2.添加taglib标识 <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
3.使用 
<c:forEach items="${names}" var ="name">
${name}<br />
</c:forEach>


文件上传
第一步:添加jar文件commons-fileupload-1.2.jar、commons-io1.3.2.jar
第二步:把form表单的enctype设置为"multipart/form",如下:
<form enctype="multipart/form-data" action="
${pageContext.request.contextPath}/xxx.action" method="post">
<input type="file" name="uploadImage"></inpput>
</form>
第三步:在Action类中添加属性,属性应对应表单文件字段的名称
public class HelloWorld(){
private File uploadImage;//得到上传的文件
private String uploadImageContentType;//得到文件的类型
private String uploadImageFileName;//得到文件的名字
/*这里省略getter,ssetter方法*/
public String upload() throws Exception{
String realPath = ServletActionContext.getServletContext()
.getRealPath("/images");
File file = new File(realPath);
if(!file.exists()){
file.mkdirs();//创建目录
}
FileUtils.copyFile(uploadImage,new File(file.uploadImageFileName));
return "success";
}
}

多文件上传

第一步:添加jar文件commons-fileupload-1.2.jar、commons-io1.3.2.jar
第二步:把form表单的enctype设置为"multipart/form",如下:
<form enctype="multipart/form-data" action="
${pageContext.request.contextPath}/xxx.action" method="post">
<input type="file" name="uploadImages" />
<input type="file" name="uploadImages" />
<input type="file" name="uploadImages" />
</form>
第三步:在Action类中添加属性,属性应对应表单总文件字段的名称
public class HelloWorld(){
private File[] uploadImages;//得到上传的文件
private String[] uploadImagesContentType;//得到文件的类型
private String[] uploadImagesFileName;//得到文件的名字
/*这里省略getter,ssetter方法*/
public String upload() throws Exception{
String realPath = ServletActionContext.getServletContext()
.getRealPath("/images");
File file = new File(realPath);
if(!file.exists()){
file.mkdirs();//创建目录
}
for(int i=0;i<uploadImages.lenth;i++){
File uploadImage = uploadImages[i];
FileUtils.copyFile(uploadImage,
new File(file.uploadImagesFileName[i]));
}
return "success";
}
}

自定义拦截器
1.要自定义拦截器需要实现
com.opensymphony.xwork2.interceptor.Intercepetor接口:
2.在intercept方法中定义拦截器
invcation.invoke();//允许通过,不拦截
3.在struts.xml中注册拦截器

public class PremissionInterceptor implements Interceptor{
private static final long serialCersionUID =-5234513456632212342L;
public void destroy(){

}
public void init(){

}
public String intercept(ActionInvoction invocation) throws Exception{
System out Println("进入拦截器");
Object user = ActionContext.getContext().getSession.get("user");

if(user != null){
//如果不为null。则用户已登陆,不拦截,余怒执行请求的action
return invovation.invoke();
}
ActionContext.getContext.put("message","你没有权限操作");

return null;

}


}
struts.xml
<package name="employee" namespace="/control/employee" extends="struts-default">

<!--定义拦截器,这个方法不推荐-->
<interceptors>
<interceptor name="permission" claass="cn.soldier.interceptor.PremissionInterceptor"></interceper>
</interceptors>

<global-results>
<resutlt name="messagee">/WEB-INF/message.jsp</resulet>
</gloval-resuts>
<action name="list_*" class="XXXXX" method={1}>
<!--使用拦截器,为action配置拦截器的这种做法
,将会丢失Struts2的其它核心拦截功能。解决这个问题的办法是
定义一个拦截器栈-->
<interceptor-ref name="permission" />
<resutlt name="success">/WEB-INF/page/employee/meaagge.jsp</tesult>
</action>
</package>
<!--
全局result(global-results)
有很多时候一个<result>可供很多<action>使用,
这时可以使用<global-results>标签来定义全局的<result>l。
执行顺序:当一个Action返回的String没有相应的<result>与之对应,
Struts2就会查找全局的<result>。
-->
/******************************************************************/
struts.xml 的改进
<package name="employee" namespace="/control/employee" extends="struts-default">
<!--定义拦截器-->
<interceptors>
<interceptor name="permission" claass=" cn.soldier.interceptor.PremissionInterceptor" />
<interceptor-stack name="premissionStack">
<interceptor-ref name="defaultStack" />
<interceptor-ref name="permission" />
</interceptor-stack>
</interceptors>

<action name="list_*" class="XXXXX" method={1}>
<resutlt name="success">/WEB-INF/page/employee/meaagge.jsp</tesult>
<interceptor-ref name="premissionStack" />
</action>
</package>

因为struts2中如文件上传,数据验证,封装请求参数到action等功能都是由系统
默认的defaultStack中的拦截器实现的,所以我们定义的拦截器需要引用系统默认
的defaultStack,这样应用才可以使用struts2框架提供的众多功能。
如果希望包下的所有action都是用自定义的拦截器,可以通过
<default-interceptor-ref name="permissionStack" />,
把拦截器定义为默认拦截器。要注意的是每个包只能指定一个默认拦截器。
另外,一旦我们为包中的某个action显式指定了某个拦截器,则默认拦截器不会器作用。

输入检验
在struts2中,我们可以实现对action的所有反法进行校验或者对action的指定方法进行校验。
对于输入校验struts2提供了两种实现方法:
1.采用手工编写代码实现
2.基于XML配置方式实现。

手工编写代码实现对action中所有方法输入校验
1.继承 ActionSupport 类
2.通过重写validate()方法检验
3.调用addFieldError()方法往新系统的fieldErrors添加校验是失败信息
(为了使用addfieldErrot()方法,action可以继承ActionSupport)
4.在strut.xml中<resul >添件 input 视图
<result name="input">/********/</resutlt>
5.在跳转的jsp文件里添加struts2的标签<s.fielderror />
(通过<s.fielderror /> 显示失败信息。)


通过重写validate()方法实现,validate()方法会校验action中所有与execute()
方法签名相同的方法。当某个数据校验失败时,我们应该调用addFieldError()方
法往新系统的fieldErrors添加校验是失败信息(为了使用addfieldErrot()方法,
action可以继承ActionSupport),如果系统的fieldErrors包含失败信息,Struts2
会将请求转发到名为input的result。在input视图中可以通过<s.fielderror />
显示失败信息。

validata使用例子:

public void validata(){
if(this.moble==null || "".equals(this.mobile.trim())){
this.addFieldError("moble","手机号码不为空");
}
}
验证是失败后,请求转发至input视图
<result name="input">/WEB-INF/page/addUser.jsp</resutlt>

在addUIUser.jsp页面中使用<s:fielderror /> 显示失败信息


添加struts2的标签库
<% @ taglib uri="/struts-tags" prefix="s" %>
使用struts2标签
<s:fielderror />


//校验正则表达式
Pattern.compile("^1[]358]\\d{9}$").macher(this.moble).maches()

手工编写代码实现对action指定方法输入检验

1.继承ActionSupport类
2.添加validateXxx()方法,里面楷写数据校验
3.调用addFieldError()方法往系统fieldErrors添加校验失败信息
(为了使用addfieldErrot()方法,action可以继承ActionSupport)
4.在strut.xml中<resul >添件 input 视图
<result name="input">/********/</resutlt>
5.在跳转的jsp文件里添加struts2的标签<s.fielderror />
(通过<s.fielderror /> 显示失败信息。)

 

通过validateXxx()方法实现,validateXxx()只会校验ation中名为Xxx的方法,
其中Xxx方法的第一个字母要大写,当某个数据校验失败时,我们应该调用
addFieldError()方法往系统fieldErrors添加校验失败信息(为了使用addfieldErrot()方法,
action可以继承ActionSupport),如果系统的fieldErrors包含失败信息,Struts2
会将请求转发到名为input的result。在input视图中可以通过<s.fielderror />
显示失败信息。

validateXxx()方法使用例子:
public String add() throws Exception( return "success");

public void validateAdd(){
if(this.moble==null || "".equals(this.mobile.trim())){
this.addFieldError("moble","手机号码不为空");
}
}
验证是失败后,请求转发至input视图
<result name="input">/WEB-INF/page/addUser.jsp</resutlt>

在addUIUser.jsp页面中使用<s:fielderror /> 显示失败信息

输入校验的流程
1.类型转换器对请求参数执行类型转换,并把转换后的值赋给action中的属性。
2.如果在执行类型转换的过程中出现异常,系统将异常信息保存到ActionContext。
conversionError拦截器将异常信息封装到filedErrors里。不管类型转换是否正常
都会进入第3步。
3.系统通过反射技术调用action中的validateXxx方法,Xxx为方法名
4.然后再调用action中的validate()方法。
5.经过上面4步后,如果系统中的fieldErrors存在错误信息(即存在错误信息学的集合size大于0),
系统自动将请求发至名称为input的视图。如果系统中的fieldErrors没有任何错误信息,
系统将执行action中的处理方法。

基于XML配置方法实现对action的所有方法进行输入检验
1.继承ActionSupport类
2.同一个包下编写ActionClassName-validation.xml检验文件

使用基于XML配置方式实现输入检验时,Action也需要继承ActionSupport。
并且提供校验文件,校验文件和action类存放在同一个包下,
文件的取名格式为:ActionClassName-validation.xml。
其中ActionClassName为action的简单类名,-validation为固定写法。

下面是校验文件的模板:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE validators PUBLIC
"-//OpenSymphonyGroup//XWorkValidator1.0.3//EN"
"http://www.opensymphony.com/xwork/xwork-validator-1.0.3.dta">
<validators>
<field name="username">
<field-validator type="requiredstring">
<param name="tirm">true</param>
<message>用户名不能为空</message>
</field>
</validators>

<field>指定action中要校验的属性
<field-validator>指定校验器,例子中的requiredstring有系统提供的。
系统提供了能满足大部分验证需求的校验器。校验器可以在xwork-xxxx.jar
中的com.opensymphony.xwork2.validator.validators下的default.xml中找到
<message>指定校验失败后的信息。如果需要国际化,可以为message指定key属性,
key的值为资源文件中的key。
在这个校验文件中,对action字符串类型的username属性经行校验,首先要求
调用trim()方法去掉空格,然后判断用户名是否为空。


其他校验器的例子
email校验器
<field-validator type="email">
<message>邮箱地址无效</message>
</field-validator>

regex:正则表达式校验器
<field-validator type="regex">
<param name="expression"><![CDATA[^1[358]\d{9}$]]></param>
<message>手机号码格式不正确</message>
</field-validator>

如果系统的fieldErrors包含失败信息,Struts2会将请求转发到名为input
的result。在input视图中可以通过<s.fielderror />标签显示失败信息。

struts2提供的的校验器列表
系统提供的校验器如下:
required(必填校验器,要求field的值不能为null)
requiredstring(必填字符串检验器,要求field的值不能为null,并且长度大于0,
默认情况下会对字符串去前后空格)
stringlength(字符串 长度校验器,要求field的值必须在指定的范围内,否则
校验失败,minLength参数指定最小长度,maxLength参数指定最大长度,
trim参数指定校验field之前是否去除字符串前后的空格)
regex(正则表达式校验器,检查白校验的field是否匹配一个正则表达式,
expression参数指定正则表达式,caseSensitive参数指定进行正则表达
式时,是否区分大小写,默认true)
int(整数校验器)
double(双精度浮点数校验器)
fieldexpression(字段OGML表达式验证器)
email(邮箱地址校验器)
url(网址校验器)
date(日期校验器)
conversion(转化校验器)
visitor(用于校验action中的复合属性)
expression(OGNL表达式校验器)

基于XML配置方式对指定action方法实现输入校验
当校验文件取名为 ActionClassName-validation.xml时,会对action中的所有处理
方法实施输入验证,如果你只需要对action中的某个action方法实施校验,那么。
校验文件的取名应为,ActionClassName-ActionName-validation.xml,其中
ActionName为strurs.xml中的action的名称。
例如,在实际应用中,常有以下配置:
<action name="user_*" class="cn.soldier.action.UserAction" method="{1}">
<result name="success">/WEB-INF/page/user/message.jsp</result>
<reult name="input">/WEB-INF/page/user/addUser.jsp</result>
</action>
UserAction中有以下两个处理方法:
public String add() throws Exception{

}

public String update() throws Exception{

}

要对add()方法实施校验,校验文件的取名为:
UserAction-user_add-validation.xml
要对update()方法实施验证,校验文件取名为:
UserAction-user_update-validation.xml

国际化
准备资源文件,资源文件命名格式如下:
baseName_language_country.propertires
baseName_language.properties
baseName.properties
其中baseName是资源文件的基本名,我们可以自定义,但language和country必须是
java支持的语言和国家例如:
中国大陆, baseName_zh_CN.properties
美国,baseName_en_US.properties

请看下面的例子
现在为应用添加两个资源文件:
第一个存放中文: soldier_zh_CN.properties
内容为 welcome=欢迎开到小兵工作室
第二个存放英文 soldier_en_US.properties
内容为 welcome=welcome to soldier

对于中文属性文件,我们编写好后,应该使用jdk提供的native2ascii命令将文件
转换为unicode编码的文件,命令的使用方式如下:
naticve2ascii 源文件.properties 目标文件.properties

配置全局资源与输出国际化信息
当准备好资源文件之后,我们可以在struts.xml中通过
struts.custom.i18n.resources常量把资源文件定义为全局资源文件,如下:
<constant name="struts.custom.i18n.resources" value="soldier" />
soldier为资源文件的基本名。

后面我们就可以在页面或在action中访问国际化信息了:
在jsp页面中使用 <s:text name="" />标签输出国际化信息
例如: <s:text name="welcome" /> name为资源文件中的key
在Action类中,可以通过继承ActionSupport,使员工getText()方法得到国际化信息,
该方法的第一个参数用于指定资源文件中的key.
在表单文件中,通过key属性指定资源文件中的key,如:
<s: textfield name="realname" key="welcome"/>

国际化--输出带占位符的国际化信息

资源文件的内容如下:
welcome={0},欢迎来到小兵工作室{1}

在jap页面中输出带占位符的国际化信息
<s:text name="welcome">
<s:param><s:property value="realname" /></s:param>
<s:param>参观</s:param>
</s:text>

在action类中获取带占位符的国际化信息,可以使用getText(String key,
Strng[] args)或getText(String aTextName,List arges)方法

国际化_配置包范围资源文件(使用方法不变。先找包资源再找全局资源)
在一个大型应用中,整个应用有大量的内容需要实现国际化,如果我们把国际化
的内容全都放置在全局资源属性文件中,显然会导致资源文件变得过于庞大,
臃肿,并且不便于维护,这个时候我们可以针对不同模块,使用包范围来
组织国际化文件。

方法如下:
在java的包下放置package_language_country.properties资源文件,
package为固定写法,处于该包及子包下的action都可以访问该资源。
当查找指定key的消息时,系统会先从package资源文件中查找,当找不到
对应的key时,才会从常理struts.custom.i18n.resources指定的资源文件
查找。

国际化_配置action范围的资源文件
我们也可以为某个action单独指定资源文件。
方法如下:
在action类所在的路径,放置ActionClassName_language_country.properties
资源文件,ActionClassName为类的简单名称。

查找顺序:Action-->包-->全局资源

当查找指定key的信息是,系统会先从ActionClassName_language_country.properties
资源文件中查找,如果没有找到对应的key,然后沿着当前包往上查找名为
package的资源文件。一直找到最好顶层包,如果还没有找到对应的key,最后
会从常量struts.custom.i18n.resources指定的资源文件中寻找。


OGNL表达式语言
OGNL是 Object Graphic Navigation Language(对象图导航语言)的缩写。它是
一个开源项目,Struts2框架使用OGNL作为默认表示式语言。
相对EL表达式,他提供了平时我们需要的一些功能,例如:

1)支持对象方法调用,如xxx.sayHello();
2)支持类静态的方法|值调用:格式为"@[类全名(包括包路径)]@[方法名|值名]"。如:

@java.lagn.String@format('foo%s','bar')--调用类静态方法

@tutorial.MyConstant@APP_NAME--访问类的静态值

3)操作集合对象


Ognl上有一个上下文概念(Context),说白了就是一个MAP结构,它实现了
java.utils.Map接口。
在Struts2中OGNL的上下文(Context)的实现者为ActionContext,
下面是上下文(Context)的结构示意图:
--->ValueStack(值栈,它是根对象)
--->parameters
OGNL Context --->request
--->session
--->application
--->attr
当Struts2接收一个请求时,会迅速创建ActionContext,ValueStack,acion,然后
把action存放进ValueStack,所以action的实例变量可以被OGLN访问。

访问上下文中的对象需要使用#符号标注命名空间,如#application、#session
另外OGNL会设定一个根对象(root对象),在Struts2中跟对象就是ValueStack(值栈)
如果要访问跟对象中对象的属性,可以省略#号,直接访问该对象的属性即可。

在Struts2中,根对象ValueStack的实现类为OgnlValueStak,该对象不是我们想象
的只存放单个值,而是存放一组对象,在OgnlValueStack类里有一个List类型root
变量,就是用它存放一组对象
--->request
--->application
OGNL Context --->OgnlValueSteck root变量[action,OgnlUtil, ...]
--->session
--->attr
--->parameters

在root变量中处于第一位的对象叫栈顶对象。通过我们在OGNL表达式里直接写上属性
名称即可访问root变量对象的属性,搜索顺序是存栈顶对象开始寻找,如果栈顶
对象不存在该属性,就会从第二个对象寻找,如果没有找到就会从第三个对象
寻找,依次往下访问,知道找到为止。

要注意的是:Struts2中OGNL表达式需要配合Sturts标签才可以使用,
如:<s:property value="name" />

由于ValueStack是Struts2中OGNL的根对象,如果用户需要访问值栈中的对象,
在jsp页面可以直接通过下面的el表达式访问ValueStack中对象的属性:
$(foo)//获得值栈中某个对象的foo属性

如果访问其他Context中的对象。由于他们不是跟对象,所以在访问时,
需要添加#前缀。

1)application对象:用于访问ServletContext,例如#application.username
或者#applicaion['usrename'],相当于调用ServletContext的
getAttribute("username")..

2)session对象。用来访问HttpSession对象。例如
#session.username或者#session['username']。
相当于调用session.getAttribute('usrename');


3)request对象。用来访问HttpServletRequest属性的MAP。例如
#request.username或者#request['username']。
相当于调用request.getAttribute('usrename');

4)parameter对象。用来访问HTTP请求的参数,例如
#parameter.username或者#parameter['username']。
相当于调用request.getParameter('usrename');

5)arr对象,用于按照page->request->session->application顺序访问其属性


采用OGNL表达式创建 List/Map集合对象
如果需要一个集合元素的时候(例如:List对象或者Map对象),可以使用OGNL中
同集合相关的表达式。
使用如下代码直接生成一个List对象
<s:set name="list" value="{'zhangming','xiao','liming'}" />
<!--<s:iterator>标签在迭代集合是有个特点,会把当前迭代的对象放置值栈顶-->
<s:iterator value="#list">
<s:property / > <br /> 
</s:iterator>

Set标签用于将某个值放入指定范围。
scope:指定变量被防止的范围,该属性可以接受 application、session、request、
page、action,如果没有这只该属性,则默放置在OGLN Context中。
value:赋给变量的值,如果没有这只该属性,则将ValueStack栈顶的值赋给变量。

生成一个map对象:
<s:set name="foobar" value="#{'foo1':'bar1','foo2','bar2'}" />
<s:iterator value="#foobar">
<s:property value="key" /> =
<s:property value="value /"> <br />
</s:iterator>

property标签

property标签用于输出指定值:
<s:set name="name" value="kk" />
<s:property value="#name" />

default属性:可选。如果要输出的值为null,则显示该属性指定的值。
escape属性:可选。指定是否格式化HTML代码
value属性:可选。 指定需要输出的属性值,如果没有指定该属性,则默认输出
ValueStack栈顶的值。
id属性:可选,指定改元素的标识

采用LGNL表达式判断对象是否存在于集合中。
对于集合类型,OGNL表达式可以使用in和not in 两个元素符号。其中,
in表达式用来判断某个元素是否在指定的集合对象中。
not in 判断某个元素是否不再指定的集合对象中。
例子如下:
in表达式
<s:if test="'foo' in {'foo','bar'}>

</s:if>
<s:else>
不在
</s:else>

not in 表达式
<s:if test="'foo' not in {'foo','bar'}">
不在
</s:if>
<s:else>

</s:else>

OGNL表达式的投影功能

除了in和not in 之外,OGNL表达式还允许使用某个规则获得对象的子集,常用的有
以下3个相关操作符。
?:获得所有符合逻辑的元素
^:获得符合逻辑的第一个元素
$:获得符合逻辑的最后一个元素

例子如下;
<s:iterator value="book{?#this.price>35}">
<s:property value="title" /> -$<s:property value="price" />
</s:iterator>
上面代码中,直接在集合后缀跟{}运算符表明用于取出该集合的子集,{}内的表达式
用于获得符合条件的元素,this指的是为了从大集合books筛选数据到集合
需要对大集合book进行迭代,this代表当前迭代的元素。本例的表达式用于
获取集合中价格大于35的书集合。

public class BookAction exetends ActionSuppor{

private List<Book> books;

@Override
public String execute(){
books = new LinkedList<Book>();
books.add(new Book("1001","CSS","34"));
books.add(new Book("1002","HTML","54"));
}
}


property标签

property标签用于输出指定值:
<s:set name="name" value="kk" />
<s:property value="#name" />

default属性:可选。如果要输出的值为null,则显示该属性指定的值。
escape属性:可选。指定是否格式化HTML代码
value属性:可选。 指定需要输出的属性值,如果没有指定该属性,则默认输出
ValueStack栈顶的值。
id属性:可选,指定改元素的标识


iterator标签
iterator标签用于对记得进行迭代,这里的集合包括list、set和数组。
<s:set name="list" value="{'xiao','da'}">
<s:iterator value="#list" status="st">
<font1 color=<s:if test="#st odd">red</s:if><s:else>blue</s:else> >
<s:property></font></s:property>
</s:iterator>
value属性:可选。指定被迭代的集合,如果没有设置该属性,则使用ValueStack
栈顶的集合
id属性:可选。指定集合里元素的id(已过时)
status属性:可选。该属性纸迭代时的IteratorStatus实例。
该实例包含如下几个方法:
int getCount()返回当前迭代了几个元素
int getIndex()返回当前迭代元素的索引
boolean isEven()返回当前被迭代索引是否为偶数
boolean isOdd()返回当前被迭代的索引是否为基数
boolean isFirst()返回当前被迭代元素是否是第一个元素
boolean isLast()返回当前被迭代元素是否是最后一个元素

if else 标签
<s:if></s:if>
<s:else></s:else>

url标签
<s:url action="helloworld_add" namespace="/test">
<s:param name="presionid" value="23" /> </s:url>
生成类似如下路径
/struts/test/helloworld_add.action?persionid=23

当标签属性值作为字符串类型处理时,"%"符号的用途是计算OGNL
表达式的值。

<s:set name="nyurl" value="http://www.baidu.com" />
<s:url value="#myurl" /> <br />
<s:url value="%{#myurl}" />

输出结果:
#myurl
http://www.foshanshop.net

表单标签<s:checkboxlist>复选框标签

如果集合为list
<s:checkboxlist name="list" list="{'JAVA','PHP'}"
value={'java'} />
生成html代码如下:
<input type="checkbox" name="list" value="java" 
checked="checked" /> <lable>JAVA</lable>
<input type="checkbox" name="list" value="php" 
/> <lable>PHP</lable>

如果集合为map
<s:checkboxlist name="map" list="#{1:'JAVA',2:'PHP',3:"CSS"}"
listkey="key" listValue="vlaue" value="1,3" />
生成html代码如下:
<input type="checkbox" name="map" value="1" 
checked="checked" /> <lable>JAVA</lable>
<input type="checkbox" name="map" value="2" 
/> <lable>PHP</lable>
<input type="checkbox" name="map" value="3" 
checked="checked" /> <lable>CSS</lable>

如果表集合存放的是javabean
<%
Person person1 = new Person(1,"aaa");
Person person2 = new Person(2,"bbb");
List<Person> list = new ArratList<Person>();
list.add(person1);
list.add(person2);

request.setAttribute("person",list);
%>
<!--Person里包含id和name这两个属性-->
<s:checkboxlist name="beans" list="#request.persons" 
listKey="id" listValue="name" />

<input type="checkbox" name="beans" value="1" /> <lable>aaa</lable>
<input type="checkbox" name="beans" value="2" /> <lable>bbb</lable>

表单标签—<s:radio> 单选框
该标签和checkboxlist复选框相同

如果表集合存放的是javabean
<s:radio name="beans" list="#request.persons" listKey="id" listValue="name" />
<input type="radio" id="beans1" name="beans" value="1" /> <lable>aaa</lable>
<input type="radio" id="beans2" name="beans" value="2" /> <lable>bbb</lable>

如果集合为map
<s:checkboxlist name="map" list="#{1:'JAVA',2:'PHP'}"
listkey="key" listValue="vlaue" value="1" />
生成html代码如下:
<input type="radio" id="map1" name="map" value="1" 
checked="checked" /> <lable for="map1">JAVA</lable>
<input type="radio" id="map2" name="map" value="2" 
/> <lable for="map2">JAVA</lable>


如果集合为list
<s:radio name="list" list="{'JAVA','PHP'}"
value={'java'} />
生成html代码如下:
<input type="radio" name="list" value="java" 
checked="checked" /> <lable>JAVA</lable>
<input type="radio" name="list" value="php" 
/> <lable>PHP</lable>


表单标签_select_下拉选择框
如果集合为list
<s:select name="list" list="{Java,PHP}" value="Java" />
<select name="list" id="list">
<option value="Java" selected="selected">Java</option>
<option value="PHP" >PHP</option>
</select>

如果表集合为javabean
<s:select name="beans" list="#request.prosons" listKey="id" listValue="name" />
<select name="beans" id="beans">
<option value="1" >第一个</option>
<option value="2" >第二个</option>
</select>

如果集合为map
<s:checkboxlist name="map" 
listkey="key" listValue="vlaue" value="1,3" />


<s:select name="map" list="#{1:'JAVA',2:'PHP',3:"CSS"}"
listKey="key" listBVlue="valie" value="1" />
<select name="map" id="map">
<option value="1" selected="selected">JAVA</option>
<option value="2" >PHP</option>
<option value="3" >CSS</option>
</select>

标签<s:token /> 防止变淡重复提交

<s:token />标签防止表单重复提交。用法如下:
第一步,在表单中加入<s:token />
<s:form action="hellowrold_other" method="post" namespace="/text">
<s:textfield name="person.name" />
<s:token />
<s:submit />
</s:form>

第二步:在struts.xml中配置 
<action name="hellowrold_*" class="cn.soldier.action.HelloWorldAction"
method="{1}">
<interceptor-ref name="defaultStack" />
<interceptor-ref name="token" />
<result name="invalid token">/WEB-INF/page/result.jsp</result>
</ation>

以上配置加入了 "token" 拦截器 和 "invalid token" 结果
因为"token"拦截器 在回话的token 和 请求的token 不一致时,将
直接返回 "invalid token" 结果 。

 

 posted on 2014-10-12 10:18  编程小兵  阅读(4276)  评论(1编辑  收藏  举报