【搭建Struts2环境】:
1、导入相关jar包;
struts2-core-2.x.x.jar:Struts2框架的核心类库
xwork-2.x.x.jar:Xwork类库,Struts2在其基础上构建
ongl-2.6.x.jar:对象图导航语言(Object Graph Navigation Language),Struts2框架通过其读写对象的属性
freemarker-2.3.x.jar:Struts2的UI标签的模板使用FreeMarker编写
commons-logging-1.1.x.jar:ASF出口的日志包,Struts2使用这个日志来支持Log4J和JDK1.4+的日志记录
commons-fileupload-1.2.1.jar:文件上传组件,Struts2.1.6版本后必须加入此文件
2、编写Struts2的配置文件;
Struts2默认的配置文件是struts.xml,该文件需要存放在WEB-INF/classes下(开发时放在src目录下即可,因为src目录下的文件在项目部署时,java文件会被自动编译成字节码文件并放到WEB-INF/classes下,而其它如xml、properties配置文件会原样放到WEB-INF/classes下),该文件的配置模板如下:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
"http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
</struts>
3、在web.xml文件中加入Struts2 MVC框架启动配置。
在Struts1中框架是通过Servlet启动的,在Struts2中是通过Filter启动的,在web.xml文件中的配置如下:
<filter>
<filter-name>
struts2
</filter-name>
<filter-class>
org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter
</filter-class>
</filter>
<filter-mapping>
<filter-name>
struts2
</filter-name>
<url-pattern>
/*
</url-pattern>
</filter-mapping>
【注】
Struts2.1.3之前的版本中用的filter-class为:
<filter-class>
org.apache.struts2.dispatcher.FilterDispatcher
</filter-class>
在StrutsPrepareAndExecuteFilter的init()方法中将会读取类路径下默认的配置文件struts.xml完成初始化操作。
struts2读取到struts.xml文件后,以javabean形式存放在内存中,以后struts2对用户的每次请求处理都将使用内存中的数据,而不是每次都读取struts.xml文件。
【struts.xml配置中包的介绍】
<package name="itcast" namespace="/test" extends="struts-default">
<action name="helloworld" class="cn.itcast.action.HelloWorldAction" method="execute">
<result name="success">/WEB-INF/page/hello.jsp</result>
</action>
</package>
Struts2框架中通过包来管理一组业务功能相关的Action,可配置多个包。
【name】:可以任意取名,但要唯一,不同于java的类包,如果其它包要继承该,可以通过该属性进行引用
【namespace】:定义包的命名空间,是该包下Action的路径的一部分,如上面例子的Action的访问路径是:/test/helloworld.action。该属性也可不配置,这样就是默认的命名空间""(空字符串)
【extends】:包之间的继承;通常每个包都应该继承【struts-default】包,这样才能使用Struts2提供的核心功能。struts-default包是在struts2-core-2.x.x.jar文件中的struts-default.xml中定义。struts-default.xml也是Struts2的默认配置文件,每次启动都会自动加载该文件。
【abstract】:包还可以通过abstract="true"定义为抽象包,抽象包中不能包含action,而只能被其它包继承。
【action】:【name】是请求名称的一部分,请求由【class】指定的action类中的【method】指定的方向来处理。
【result】:【name】视图名称,标签之间内容为视图路径。
【注】在视图页面可直接通过EL表达式取出Action类中的属性
如:HelloWorldAction类中
package cn.itcast.action;
public class HelloWorldAction {
private String message;
public String execute() {
message = "我的第一个Struts2应用";
return "success";
}
public String getMessage() {
return message;
}
}
在视图页面就可以通过${message}来取出相应的值,前提Action中必须有getMessage()方法,与成员变量名称无关。成员变量也可以是private String msg;所以EL表达式取值时所用的名称是getter方法后面的首字母小写的名称(一般是这样,有特殊的)。
【struts.xml文件不提示的问题】
如果快捷键设置没问题且无冲突,那就是因为配置文件没有对应的DTD(Document Type Definition)约束文档
解决方法:【Window】——【Preferences】——【MyEclipse Enterprise Workbench】——【Files and Editors】——【XML】——【XML Catalog】点击【Add】:通过【Location】指定DTD文档,【Key Type】选URI,【Key】写http://struts.apache.org/dtds/struts-2.3.dtd
【Action名称的搜索】
例如url是:http://server/struts2/path1/path2/path3/test.action
寻找namespace为"/path1/path2/path3"的package,如果不存在则寻找namespace为"/path1/path2"的package,如果还不存在则寻找namespace为"/path1"的package,如果还不存在则寻找namespace为"/"的package,如果还不存在则寻找namespace为""的package……这样逐级向上查询,如果一直找不到则页面提示找不到action;
当然如果中间找到相应的package,则会在相应的package下寻找test的action,如果在相应的package中找不到则会去默认包(namespace为""或不指定namespace的package)中寻找,如果一直找不到则页面提示找不到action
【请求直接转向页面时,不用指定class、method】
<action name="addUI">
<result>/WEB-INF/page/employee.jsp</result>
</action>
【Action类的编写】
有3种:
1、有public String execute()方法的任意Java类
2、实现Action接口,并实现public String execute()方法
3、继承ActionSupport类,并重写public String execute()方法
常用第3种,其实ActionSupport类就是Action的实现类,而且里面封装了许多有用的方法。
【Action配置中的各项默认值】
<package name="itcast" namespace="/test" extends="struts-default">
<action name="helloworld" class="cn.itcast.action.HelloWorldAction" method="execute">
<result name="success">/WEB-INF/page/hello.jsp</result>
</action>
</package>
1、如果没有为acion指定class,默认是
ActionSupport
2、如果没有为acion指定method,默认执行action中的 execute()方法
3、如果没有指定result的name属性,默认值为 success
【默认action配置】
当我们请求一个项目,而不指定具体的action名称时,会由defautl-action来处理
<default-action-ref name="login" />
该标签放在<package>标签对中
【Action异常处理】
通过
<exception-mapping result="error" exception="java.sql.SQLException" />
配置来处理
result:出现异常时转向的视图
exception:所要针对处理的异常类的全名
<package name="itcast" namespace="/test" extends="struts-default">
<action name="helloworld" class="cn.itcast.action.HelloWorldAction" method="execute">
<result name="success">/WEB-INF/page/hello.jsp</result>
<exception-mapping result="error" exception="java.sql.SQLException" />
<result name="error">/error.jsp</result>
</action>
</package>
当然也可以配置成全局的异常处理(与配置全局的视图方法一样。)
【Action中result的各种转发类型】
result标签有一个type属性,表示视图转发类型:一般有:dispatcher、chain、redirect、redirectAction、plainText
dispatcher:
转发【默认值】,只能转发到jsm或html页面,不能转发到一个action
chain:
转发到一个action
redirect:
重定向到jsp或html页面,重定向的视图不能在WEB-INF路径下。
视图url后可通过ongl表达式获取action类的属性,如:
<action name="list" class="cn.itcast.action.HelloWorldAction" method="execute">
<result name="success" type="redirect">
/employeeAdd.jsp?username=${username}
</result>
</action>
HelloWorldAction类中必须对应的username字段和getUsername(),这样在employeeAdd.jsp页面即可通过EL表达式${param.username}来获取传递过来的值
当参数是中文时,需要在HelloWorldAction类中对其进行编码:
username = URLEncoder.encode("中国", "utf-8");
在视图页面通过
<%
String name = request.getParameter("username");
name = new String(name.getBytes("iso-8859-1"),"utf-8");
name = URLDecoder.decode(name,"utf-8");
%>
<%=name%>
redirectAction:
重定向到某个action
result标签对中间的内容为所要重定向到的action标签的name属性值,如:
<action name="list" class="cn.itcast.action.HelloWorldAction" method="execute">
<result name="success" type="redirect">
/employeeAdd.jsp?username=${username}
</result>
</action>
<action name="redirectActionDemo">
<result type="redirectAction">
list
</result>
</action>
当用户请求redirectActionDemo.action时会重定向到name属性为list的action,由其进行处理。
当要重定向的action不在当前包中时的配置如下:
<action name="redirectActionDemo">
<result type="redirectAction">
<param name="namespace">所要重定向的action所在包的namespace属性值</param>
<param name="actionName">所要重定向的action的name属性值</param>
</result>
</action>
plainText:
原样输出视图页面的代码,当我们想要显示jsp文件源代码时,可以用此类型。
<action name="plainText">
<result type="plainText">
/index.jsp
</result>
</action>
当视图页面有中文要显示时,需要在配置文件中注入location、charSet参数:
<action name="plainText">
<result type="plainText">
<param name="location">/index.jsp</param>
<param name="charSet">utf-8</param>
</result>
</action>
【全局视图的配置】
<global-results>
<result name="message">/WEB-INF/page/message.jsp</result>
</global-results>
【注】该标签要放在packge标签对之间,且在action标签之前
转向全局视图的action中不需要再指定result
<action name="manage" class="cn.itcast.action.HelloWorldAction" method="add">
</action>
add方法返回到视图message
【这种配置中是实现了在某个包中的全局视图,如何实现在整个应用中的全局视图?】
可以单独定义一个包base存放全局视图,继承struts-default,再让其它包都继承base,如:
<package name="base" extends="struts-default">
<global-results>
<result name="message">/WEB-INF/page/message.jsp</result>
</global-results>
</package>
<package name="itcast" extends="base">
......
</package>
【${}动态视图、url带参数视图】
视图指向页面由Action的属性值动态指定,如:
==============================================================================================
<action name="user" class="com.bjsxt.struts2.user.action.UserAction">
<result>${r}</result>
</action>
==============================================================================================
${r}是从ValueStack中取出r对应的值
请求一个Action时,Struts2会自动创建对象并封装数据,并将该对象放入ValueStack中
UserAction的代码:
public class UserAction extends ActionSupport {
private int type;
private String r;
//此处省去2个字段的getter,setter方法
public String execute() {
if (type==1) r = "/user_success.jsp";
if (type==2) r = "/user_error.jsp";
return "success";
}
}
==============================================================================================
基于上面的思想,可以设置带有参数的视图,当然参数也是从ValueStack中的对象中取出
<action name="user" class="com.bjsxt.struts2.user.action.UserAction">
<result>/user_success.jsp?type=${type}</result>
</action>
这样,我们在请求这个Action的时候得给它传递过去type值,或由Action经过一些逻辑判断自动生成值
另:一次请求涉及到的所有Action共用一个ValueStack
所以当一个Action_A的视图forward另一个Action_B时指向的视图要用Action_A中的值时,可以直接用,
而不用我们手动将值传给Action_B
多次请求之间就需要手动传递参数了,如redirect情况下
<action name="user" class="com.bjsxt.struts2.user.action.UserAction">
<result type="redirect">/user_success.jsp?t=${type}</result>
</action>
这里redirect的目标是一个jsp而不是Action,所以这次请求后ValueStack是空的,
我们就不能通过<s:property value="t" />来取了,需要从ActionContext中的parameters对象中取
<s:property value="#parameters.t" />
==============================================================================================
【为Action的属性注入值】
在action标签对之间加入param标签,如下:
<param name="savepath">...value...</param>
name属性值是action类中的属性,必须有对应的setter方法,这样就可以给该属性指定值,而且也可以在其转发的页面中通过EL表达式${savepath}取出该值。
一些灵活变化的值可以通过该方法在配置文件中动态指定。
【指定需要Struts2处理的请求后缀】
一般都默认使用.action的后缀访问Action。其实默认后缀可以通过常量"struts.action.extension"
我们可以配置Struts2只处理以.do为后缀的请求路径:
<constant name="struts.action.extension" value="do" />
如果需要指定多个请求后缀,多个后缀之间用英文的分号隔开。如:
<constant name="struts.action.extension" value="action,do,go" />
【注】这样指定后缀以后,必须带上所指定的后缀访问,而不指定的话,可以加上.action也可不加
【细说常量定义】
常量可以在struts.xml或struts.properties中配置,建议在struts.xml中配置;
在struts.xml中的配置:
<constant name="struts.action.extension" value="do" />
在struts.properties中的配置:
struts.action.extension=do
常量可以在下面多个配置文件中进行定义,Struts2加载常量的搜索顺序:
struts-default.xml
struts-plugin.xml
struts.xml
struts-properties
web.xml
如果在多个配置文件中配置了同一个常量,则后一个会覆盖前面文件中配置的常量。
【常用常量介绍】
internationalization即 i18n
Struts2涉及的常量名可通过org.apache.struts2.deafult.properties文件获取
指定默认编码集,作用于HttpServletRequest的setCharacterEncoding方法和freemarker、velocity的输出
<constant name="struts.i18n.encoding" value="utf-8" />
指定请求后缀,多个后缀之间用英文的分号隔开。如:
<constant name="struts.action.extension" value="action,do,go" />
设置浏览器是否缓存静态内容,默认值为true(生产环境下使用)开发阶段最好关闭
<constant name="struts.serve.static.browserCache" value="false" />
设置当struts的配置文件修改后是否重新加载该文件,默认值为false(生产环境下使用)开发阶段最好打开
<constant name="struts.configuration.xml.reload" value="true" />
开发模式下使用可以打印出更详细的错误信息
<constant name="struts.devMode" value="true" />
默认的视图主题
<constant name="struts.ui.theme" value="simple" />
与Spring集成时,指定由Spring负责action对象的创建
<constant name="struts.objectFactory" value="spring" />
设置Struts2是否支持动态方法使用,该属性的默认值是true,设置为false则关闭动态方法的使用
<constant name="struts.enable.DynamicMethodInvocation" value="true" />
上传文件的总的大小限制
<constant name="struts.multipart.maxSize" value="10701096" />
使用OGNL表达式访问静态方法的开关,默认为false
<constant name="struts.ognl.allowStaticMethodAccess" value="true" />
【Struts2的处理流程】
用户请求
——>StrutsPrepareAndExecuteFilter
——>Interceptor(Struts2内置的一些拦截器或用户自定义拦截器)
——>Action(用户编写的Action类,类似Struts1中的Action)
——>Result(类似Struts1中的forward)
——>Jsp/Html
——>响应
StrutsPrepareAndExecuteFilter是Struts2框架的核心控制器,它负责拦截由<url-pattern>/*</url-pattern>指定的所有用户请求,当用户请求到达时,该Filter会过滤用户的请求,默认情况下,如果用户请求的路径不带后缀或者后缀以.action结尾,这时请求将被转入Struts2框架处理,否则Struts2框架将略过该请求的处理。当请求转入Struts2框架处理时会先经过一系列的拦截器,然后再到Action。
与Struts1不同,Struts2对用户的每一次请求都会创建一个Action,所以Struts2中的Action是线程安全的。
【为应用指定多个struts配置文件】
在struts.xml文件中通过include标签元素指定多个配置文件:
<struts>
<include file="struts-user.xml" />
<include file="struts-order.xml" />
</struts>
一般我们不在struts.xml文件中指定action,都是包含相应的xml文件(就像main方法中不写详细代码,而只调用方法)
【动态方法调用】(在2.1版本之后就不推荐使用了)
当Action中存在多个方法时,我们可以通过【!+方法名】调用指定方法。如:
所请求的action的url路径为:http://localhost:8080/struts/test/helloworld.action
该action中有execute()、other()方法
默认调用的是execute(),如果要调用other(),则通过以下url:
http://localhost:8080/struts/test/helloworld!other.
可以通过
<constant name="struts.enable.DynamicMethodInvocation" value="false" />
关闭动态方法的使用
【使用通配符定义action】
<package name="itcast" namespace="/test" extends="struts-default">
<action name="helloworld_*" class="cn.itcast.action.HelloWorldAction" method="{1}">
<result name="success">/WEB-INF/page/hello.jsp</result>
</action>
</package>
[name="helloworld_*"]:下划线可以不要[name="helloworld*"],这里只是方便查看
[method="{1}"]:{1}是与前面的*匹配,*可以有多个,所以通过序号来对应,{1}可以在action标签中的任意地方使用来动态改变相应内容,如/WEB-INF/page/hello{1}.jsp
如:请求/test/helloworld_other.action时,*对应other,{1}也就相当于other
或:请求/test/helloworldother.action时,*对应other,{1}也就相当于other
【接收请求参数】
一、采用基本类型接收请求参数(get/post)
在Acion类中定义与请求参数同名的属性,Struts2通过反射调用与请求参数同名的属性的setter方法来给相应属性赋值。get/post方式均可,所以可以直接把表单提交的数据与Action类属性对应
如Action类中有id,name属性,则
<form action="...">
ID:<input type="text" name="id" />
name:<input type="text" name="name" />
</form>
这样提交的数据会自动封装到Action类相应的属性中。
二、采用复杂类型接收参数
Action类中有一复杂属性private Person person,Person类有2个属性:id,name,则:
<form action="...">
ID:<input type="text" name="person.id" />
name:<input type="text" name="person.name" />
</form>
在视图页面取出相应的值:${person.id} ${person.name}
如果我们不传递任何参数,那Action中的person属性将为null,
如果传递了,则Struts2将会自动帮我们创建person对象,并封装数据
Action中的Person对象可以通过传递参数让Struts2帮我们创建,也可以手动new,但Person
类中一定要有无参的构造方法。
三、ModelDriven模式
Action类实现ModelDriven<T>接口(Person类有2个属性:id,name)
实现getModel()方法
public class PersonAction extends ActionSupport implements ModelDriven<Person> {
private Person person = new Person();//这里必须手动new出对象
public Person getModel() {
return person;
}
……
}
<form action="...">
ID:<input type="text" name="id" />
name:<input type="text" name="name" />
</form>
这时,用这种简单的表单就能实现参数的接收。
原理:请求该Action时,会调用getModel方法获取person对象,往person中封装相应数据
【中文乱码问题】
首先:在struts.xml文件中添加:
<constant name="struts.i18n.encoding" value="utf-8" />
get方式传递的中文数据,需要在Action类中进行编码:
URLEncoder.encode(name,"utf-8");
在视图端再解码:
URLDecoder.decode(request.getParameter("name"),"utf-8");
【自定义类型转换器】
继承DefaultTypeConverter(最好选com.opensymphony.xwork2.conversion.impl)
重写public Object convertValue(Map<String, Object> context, Object value, Class toType)
public class DateTypeConverter extends DefaultTypeConverter {
@Override
public Object convertValue(Map<String, Object> context, Object value,
Class toType) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");
try {
if (toType == Date.class) {//字符串向Date类型转换
String[] params = (String[]) value;
return dateFormat.parse(params[0]);
} else if (toType == String.class) {//Date向字符串类型转换
Date date = (Date) value;
return dateFormat.format(date);
}
} catch (ParseException e) {
}
return null;
}
}
或继承StrutsTypeConverter(该类是DefaultTypeConverter的子类)
重写public Object convertFromString(Map context, String[] values, Class toType)
重写public Object convertToString(Map context, Object o)
一、局部
在Action类所在包下放置ActionClassName-conversion.properties文件,
ActionClassName是Action的类名,后面的-conversion.properties是固定写法,
以HelloWorldAction为例,
在HelloWorldAction包下放置HelloWorldAction-conversion.properties文件,内容为:
birthday=cn.itcast.type.converter.DateTypeConverter
左边是要转换的类的属性,右边是转换器的完整路径名
二、全局
在WEB-INF/classes下(即src目录下)放置xwork-conversion.properties文件。内容:
java.util.Date=cn.itcast.type.converter.DateTypeConverter
左边是待转换的数据类型,右边是转换器的完整路径名
【获取Map类型的request/session/application属性】
方法一:通过ActionContext获取
public class HelloWorldAction {
public String execute() {
ActionContext ctx = ActionContext.getContext();
Map<String, Object> application = ctx.getApplication();
Map<String, Object> session = ctx.getSession();
Map<String, Object> request = ctx.get("request");
application.put("application","应用范围");
session.put("session","session范围");
request.put("request","request范围");
return "message";
}
}
在JSP页面就可以通过EL表达式获取相应作用域中的属性
${applicationScope.application}
${sessionScope.session}
${requestScope.request}
或通过
<s:property value="#application.application" />
<s:property value="#session.session" />
<s:property value="#request.request" />
方法二:(最常用的方法,最常用的是session)
DI(dependency injection)依赖注入 IoC(Inverse of Control)控制反转
实现指定接口,由struts框架运行时加入
public class HelloWorldAction implements RequestAware,SessionAware,ApplicationAware {
private Map<String, Object> request;
private Map<String, Object> session;
private Map<String, Object> application;
public void setRequest(Map<String, Object> request) {
this.request = request;
}
public void setSession(Map<String, Object> session) {
this.session = session;
}
public void setApplication(Map<String, Object> application) {
this.application = application;
}
}
【获取HttpServleRequest、HttpSession、ServletContext、HttpServletResponse对象】
方法一:通过ServletActionContext类直接获取
public String rsa() throws Exception {
HttpServletRequest request = ServletActionContext.getRequest();
ServletContext sc = ServletActionContext.getServletContext();
HttpSession session = request.getSession();
HttpServletResponse response = ServletActionContext.getResponse();
return "scope";
}
方法二:实现指定接口,由struts框架运行时加入
public class HelloWorldAction implements ServletRequestAware,ServletResponseAware,ServletContextAware {
private HttpServletRequest request;
private HttpServletResponse response;
private ServletContext servletContext;
//或只实现ServletRequestAware,由request对象再获取session,application
//request.getSession();request.getServletContext();
public void setServletRequest(HttpServletRequest request) {
this.request = req;
}
public void setServletResponse(HttpServletResponse response) {
this.response = res;
}
public void setServletContext(ServletContext servletContext) {
this.servletContext = servletContext;
}
}
【文件上传】
1、需要的jar包:commons-fileupload-1.2.1.jar、commons-io.1.3.2.jar
2、设置form表单的 enctype="multipart/form-data",如:
<form enctype="multipart/form-data" action="${pageContext.request.contextPath}/xxx.action" method="post">
<input type="file" name="uploadImage" />
</form>
3、在对应的Action类中添加上传文件所对应的属性:示例:
public class HelloWorldAction {
private File uploadImage;
private String uploadImageContentType;
private String uploadImageFileName;
public String upload() throws Exception {
String realpath = ServletActionContext.getServletContext().getRealPath("/images");
File file = new File(realpath);
if (uploadImage != null) {
if (!file.exists()) {
file.mkdirs();
}
FileUtils.copyFile(uploadImage,new File(file, uploadImageFileName));
return "success";
}
return "failure";
}
}
注意:1、表单中文件域的名称要和Action类中的属性名一致;
2、Action中还要有文件的另外2个属性:类型、文件名,都是固定格式;类型:在属性名后加ContentType;文件名:在属性名后加FileName。
这里:表单中文件域的名称和Action类中的属性名为uploadImage,
Action类中的类型名为:uploadImageContentType、文件名为uploadImageFileName。
上传文件的总的大小限制的常量配置
<constant name="struts.multipart.maxSize" value="10701096" />
【多文件上传】
与单文件上传不同的是,在Action类中将文件属性定义为数组类型或List类型
public class HelloWorldAction {
private File[] uploadImages;
private String[] uploadImagesContentType;
private String[] uploadImagesFileName;
public String upload() throws Exception {
String realpath = ServletActionContext.getServletContext().getRealPath("/images");
File file = new File(realpath);
if (uploadImages != null) {
if (!file.exists()) {
file.mkdirs();
}
for (int i=0; i<uploadImages.length; i++) {
File uploadImage = uploadImages[i];
FileUtils.copyFile(uploadImage,new File(file, uploadImagesFileName[i]));
return "success";
}
}
return "failure";
}
}
【自定义拦截器】
要实现com.opensymphony.xwork2.interceptor.Interceptor接口。
并实现其中的3个方法:
public void destroy();
public void init();
public String interceptor(ActionInvocation invocation) throws Exception;
最主要的就是interceptor方法,这是对访问Action类中的方法的拦截
通过invocation.invoke()方法,可以使被拦截的方法正常执行,所以可以通过一些逻辑判断和条件限制,来对访问进行过滤拦截
代码示例:
import com.opensymphony.xwork2.interceptor.Interceptor;
public calss PermissionInterceptor implements Interceptor {
public void destroy() {}
public void init() {}
public String interceptor(ActionInvocation invocation) throws Exception {
ActionContext context = ActionContext.getContext();
Object obj = context.getSession().get("user");
if (user != null) {//如果用户登录了,就不对其请求进行拦截
return invocation.invoke();//让所要处理请求的方法正常执行,且会返回到相应的视图
} else {
context.put("message", "你没有权限执行该操作!");//往request作用域中放置参数
return "message";//返回出错的message视图。
}
}
}
【自定义拦截器注册信息的配置】
Action类使用了自定义的拦截器以后,就会失去Struts2核心的拦截器功能,所以配置信息的时候,应该配置一个拦截器堆,将Struts2核心的拦截器功能添加进去。Struts2的默认核心拦截器堆:defaultStack
配置时defaultStack应该在拦截堆中的第一个位置。
配置示例:
==============================================================================================
<package name="itcast" namespace="/control/employee" extends="struts-default">
<interceptors>
<interceptor name="permission"
class="cn.itcast.interceptor.PermissionInterceptor">
</interceptor>
<interceptor-stack name="permissionStack">
<interceptor-ref name="defaultStack"></interceptor-ref>
<interceptor-ref name="permission"></interceptor-ref>
</interceptor-stack>
</interceptors>
<action name="list*" class="cn.itcast.action.HelloWorldAction" method="{1}">
<param name="savepath">/images</param>
<result name="success">/WEB-INF/page/hello.jsp</result>
<interceptor-ref name="permissionStack"></interceptor-ref>
</action>
</package>
==============================================================================================
<interceptors>
<interceptor name="permission"
class="cn.itcast.interceptor.PermissionInterceptor">
</interceptor>
<interceptor-stack name="permissionStack">
<interceptor-ref name="defaultStack"></interceptor-ref>
<interceptor-ref name="permission"></interceptor-ref>
</interceptor-stack>
</interceptors>
拦截器注册信息都写在interceptors标签对之间;下面还有两个标签对:
interceptor标签对:注册自定义的标签,name属性对应一个自定义名称,class属性对应其java类(带包名)
interceptor-stack标签对:拦截器堆,能定义多个拦截器,Action类可以关联拦截器堆,而使其中的每个
拦截器都作用于该Action类;
其下面是interceptor-ref标签对:通过name属性与默认的、自定义的拦截器(堆)关联。
==============================================================================================
<action name="list*" class="cn.itcast.action.HelloWorldAction" method="{1}">
<param name="savepath">/images</param>
<result name="success">/WEB-INF/page/hello.jsp</result>
<interceptor-ref name="permissionStack"></interceptor-ref>
</action>
拦截器注册好后,要在使用拦截器的Action中进行配置关联,通过interceptor-ref标签对进行关联
其name属性,应该是注册好的拦截器(堆)的name属性值。
==============================================================================================
如果希望包下的所有Action都使用自定义的拦截器,可以通过
<default-interceptor-ref name="permissionStack" />
把拦截器定义为默认的,而不必给每一个Action中都关联拦截器;
每个包只能定义一个默认的拦截器;
当然如果为某个Action专门指定了拦截器,那默认的拦截器将对其失效。
配置示例:
<package name="itcast" namespace="/control/employee" extends="struts-default">
<interceptors>
<interceptor name="permission"
class="cn.itcast.interceptor.PermissionInterceptor">
</interceptor>
<interceptor-stack name="permissionStack">
<interceptor-ref name="defaultStack"></interceptor-ref>
<interceptor-ref name="permission"></interceptor-ref>
</interceptor-stack>
</interceptors>
<default-interceptor-ref name="permissionStack" />
<action name="list*" class="cn.itcast.action.HelloWorldAction" method="{1}">
<param name="savepath">/images</param>
<result name="success">/WEB-INF/page/hello.jsp</result>
</action>
</package>
若要为某个Action额外增加拦截器,而又不想失去默认拦截器的功能:
可以把默认的拦截器在Action中再指定一次,之后再增加额外的拦截器。
==============================================================================================
【输入校验】
可以对Action的所有方法或指定方法进行校验。
有2种方法:
1、手工编写代码实现
2、基于XML配置文件实现
【手工编写代码对所有方法进行输入校验】
一、继承ActionSupport类并重写其public void validate()方法,
该方法会校验Action中所有与execute方法签名相同的方法,当某个数据校验失败时,
我们应该调用addFieldError(String fieldName, String errorMessage)方法
往系统的fieldError添加校验失败信息。
二、为Action配置一个input视图返回原页面
如果系统的fieldError包含失败信息,Struts2会将请求转发到名为input的result。
三、在result视图中可以通过<s:fielderror />显示失败信息。
在JSP页面中引入相应的标签库: <%@ taglib uri="/struts-tags" prefix="s"%>
并在form表单前面使用: <s:fielderror />
【<s:fielderror>标签】
<s:fielderror />会自动以列表形式把通过addFieldError方法加入的错误信息输出到页面上,不好
的是,输出的时候已经指定了CSS样式,不便于页面样式的操作。
<s:fielderror fieldName="fieldName" />也可只输出指定的字段的错误信息。
可通过<s:property value="errors.fieldName[index]">取出指定的不带CSS的错误信息文本
其中,errors.fielaName[index]是OGNL表达式
errors是值栈ValueStack中的对象名,可通过它获取其值,它的值是以Map<fieldName,array>形式存放
fieldName是通过addFieldError方法添加错误信息时指定的出错的fieldName。
index是保存错误信息的数组array的下标,同一个字段可添加多个错误信息
【通过<s:debug />可查看ValueStack详情】
代码示例:PersonAction类有两个属性:username、mobile
public void validate() {
if (this.username == null || "".equals(this.username.trim())) {
this.addFieldError("username", "用户名不能为空");
}
if (this.mobile == null || "".equals(this.mobile.trim())) {
this.addFieldError("mobile", "手机号不能为空");
} else {
if (!Pattern.compile("^1[358]\\d{9}$").matcher(this.mobile).matches()) {
this.addFieldError("mobile", "非法的手机号");
}
}
}
【手工编写代码对指定方法进行输入校验】
一、继承ActionSupport类(主要是为了使用addFieldError()方法),编写public void validateXxx()方法
validateXxx()方法只会校验Action中方法名为xxx的方法,
如:针对add()方法,编写validateAdd()方法
当某个数据校验失败时,我们应该调用addFieldError(String fieldName, String errorMessage)
方法往系统的fieldError添加校验失败信息。
二、为Action配置一个input视图返回原页面
如果系统的fieldError包含失败信息,Struts2会将请求转发到名为input的result。
三、在result视图中可以通过<s:fielderror />显示失败信息。
在JSP页面中引入相应的标签库: <%@ taglib uri="/struts-tags" prefix="s"%>
并在form表单前面使用: <s:fielderror />
【输入校验的流程】
1、类型转换器对请求参数执行类型转换,并把转换后的值赋给Action类中的属性;
2、如果在执行类型转换的过程中出现异常,系统会将异常信息保存到ActionContext,conversionError拦截器
将异常信息封闭到fieldError里,不管类型转换是否出现异常,都会进入第3步;
3、系统通过反射技术先调用Action中的validateXxx()方法;
4、再调用validate()方法;
5、经过上面4步,如果系统中的fieldError存在错误信息(即存放错误信息的集合的siz>0),系统自动将请求转发
至名为input的视图。如果系统中的fieldError没有任何错误信息,系统将执行Action中的处理方法。
【基于XML文件配置实现对所有方法进行输入校验】
一、Action类继承ActionSupport类
二、提供校验文件,该文件和Action类在同一个包下,命名格式:ActionClassName-validation.xml
ActionClassName是Action类的名称,以UserAction类为例:UserAction-validation.xml
==============================================================================================
配置示例:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE validators PUBLIC
"-//Apache Struts//XWork Validator 1.0.2//EN"
"http://struts.apache.org/dtds/xwork-validator-1.0.2.dtd">
<validators>
<field name="username">
<field-validator type="requiredstring">
<param name="trim">true</param>
<message key="requiredstring">用户名不能为空</message>
</field-validator>
</field>
<field name="mobile">
<field-validator type="requiredstring">
<message>号码不能为空</message>
</field-validator>
<field-validator type="regex">
<param name="regex"><![CDATA[^1[358]\d{9}$]]></param>
<message>手机格式不正确</message>
</field-validator>
</field>
</validators>
==============================================================================================
<field>指定Action中要校验的属性,<field-validator>指定校验器,上面的校验器requiredstring是由
系统提供的,系统提供了能满足大部分验证需求的校验器,这些校验器的定义可以在xwork2-2.jar中的
com.opensymphony.xwork2.validator.validators下的default.xml中找到。
<message>为校验失败后的提示信息,如果需要国际化,可以为message指定key属性,key的值为资源文件中
的key。
在这个校验文件中,对Action中字符串类型的username属性进行校验,首先用trim方法去掉空格,再判断其
是否为空(注入默认值为true)。
==============================================================================================
【系统提供的校验器】
required (必填校验器,要求field的值不能为null)
requiredstring (必填字符串校验器,要求field的值不能为null,并且长度大于0,默认情况下会对字符串去前后空格)
stringlength (字符串长度校验器,要求field的值必须在指定的范围内,否则校验失败,
minLength参数指定最小长度,
maxLength参数指定最大长度,
trim参数指定校验field之前是否去除字符串前后的空格)
regex (正则表达式校验器,检查被校验的field是否匹配一个正则表达式.
expression参数指定正则表达式
(实际操作中通过regex参数指定正则表达式成功,expression失败),
caseSensitive参数指定进行正则表达式匹配时,是否区分大小写,默认值为true)
int (整数校验器,要求field的整数值必须在指定范围内,min指定最小值,max指定最大值)
double (双精度浮点数校验器,要求field的双精度浮点数必须在指定范围内,min指定最小值,max指定最大值)
fieldexpression (字段OGNL表达式校验器,要求field满足一个ognl表达式,expression参数指定ognl表达式,该逻辑表达式基于 ValueStack进行求值,返回true时校验通过,否则不通过)
email (邮件地址校验器,要求如果field的值非空,则必须是合法的邮件地址)
url (网址校验器,要求如果field的值非空,则必须是合法的url地址)
date (日期校验器,要求field的日期值必须在指定范围内,min指定最小值,max指定最大值)
conversion (转换校验器,指定在类型转换失败时,提示的错误信息)
visitor (用于校验action中的复合属性,它指定一个校验文件用于校验复合属性中的属性)
expression (OGNL表达式校验器,expression参数指定ognl表达式,该逻辑表达式基于ValueStack进行求值,返回true时校验通 过,否则不通过,该校验器不可用在字段校验器风格的配置中)
【校验器的使用例子】
required 必填校验器
<field-validator type="required">
<message>性别不能为空!</message>
</field-validator>
requiredstring 必填字符串校验器
<field-validator type="requiredstring">
<param name="trim">true</param>
<message>用户名不能为空!</message>
</field-validator>
stringlength:字符串长度校验器
<field-validator type="stringlength">
<param name="maxLength">10</param>
<param name="minLength">2</param>
<param name="trim">true</param>
<message><![CDATA[产品名称应在2-10个字符之间]]></message>
</field-validator>
email:邮件地址校验器
<field-validator type="email">
<message>电子邮件地址无效</message>
</field-validator>
regex:正则表达式校验器
<field name="mobile">
<field-validator type="requiredstring">
<message>号码不能为空</message>
</field-validator>
<field-validator type="regex">
<param name="regex"><![CDATA[^1[358]\d{9}$]]></param>
<message>手机格式不正确</message>
</field-validator>
</field>
int:整数校验器
<field-validator type="int">
<param name="min">1</param>
<param name="max">150</param>
<message>年龄必须在1-150之间</message>
</field-validator>
字段OGNL表达式校验器
<field name="imagefile">
<field-validator type="fieldexpression">
<param name="expression"><![CDATA[imagefile.length() <= 0]]></param>
<message>文件不能为空</message>
</field-validator>
</field>
【基于XML文件配置实现对指定方法进行输入校验】
在对所有方法进行输入校验的基础上,把校验文件的名称更改为:
ActionClassName-ActionName-validation.xml
ActionClassName还是Action类的不带包的名称
ActionName是struts.xml文件中配置的action标签的name属性值
==============================================================================================
示例:
<action name="user_*" class="cn.itcast.action.Useraction" method="{1}">
<result name="success">/WEB-INF/page/message.jsp</result>
<result name="input">/WEB-INF/page/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
==============================================================================================
【基于XML校验的一些特点】
当为某个Action提供了ActionClassName-validation.xml、ActionClassName-ActionName-validation.xml
两种规则的校验文件时,系统会按下面顺序寻找校验文件:
ActionClassName-validation.xml
ActionClassName-ActionName-validation.xml
把所有的校验规则汇总,有冲突的以后面文件的为准
当Action继承了另一个Action,父类的校验文件会被先搜索到,接下来是子类的,把校验规则汇总。
【国际化】
国际化资源文件的命名格式:
baseName_language_country.properties
baseName_language.properties
baseName.properties
其中baseName是资源文件的基本名,我们可以自定义,但language和country必须是java支持的语言和国家。如:
中国大陆: baseName_zh_CN.properties
美国: baseName_en_US.properties
现在为应用添加两个资源文件:
第一个存放中文:itcast_zh_CN.properties
内容为:welcome=欢迎来到传智播客
第二个存放英语(美国): itcast_en_US.properties
内容为: welcome=welcome to itcast
对于中文的属性文件,我们编写好后,应该使用jdk提供的native2ascii命令把文件转换为unicode编码的文件。
命令的使用方式如下:
native2ascii 源文件.properties 目标文件.properties
【配置全局资源与输出国际化信息】
当准备好资源文件之后,我们可以在struts.xml中
通过struts.custom.i18n.resources常量把资源文件定义为全局资源文件,如下:
<constant name="struts.custom.i18n.resources" value="itcast" />
itcast为资源文件的基本名。
后面我们就可以在页面或在action中访问国际化信息:
在JSP页面中使用<s:text name=""/>标签输出国际化信息:
<s:text name="user"/>,name为资源文件中的key
或:<s:property value="getText('name')" />
在Action类中,可以继承ActionSupport,
使用getText(String key)方法得到国际化信息,该方法的参数用于指定资源文件中的key。
在表单标签中,通过key属性指定资源文件中的key,如:
<s:textfield name="realname" key="user"/>
【国际化—输出带占位符的国际化信息】
资源文件中的内容如下:
welcome= {0},欢迎来到传智播客{1}
在jsp页面中输出带占位符的国际化信息
<s:text name="welcome">
<s:param><s:property value="realname"/></s:param>
<s:param>学习</s:param>
</s:text>
在Action类中获取带占位符的国际化信息,可以使用
getText(String key, String[] args)或getText(String aTextName, List args)方法。
【国际化—包范围资源文件】
在一个大型应用中,整个应用有大量的内容需要实现国际化,如果我们把国际化的内容都
放置在全局资源属性文件中,显然会导致资源文件变的过于庞大、臃肿,不便于维护,这
个时候我们可以针对不同模块,使用包范围来组织国际化文件。
方法如下:
在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指定的资源文件中寻找
【国际化—JSP中直接访问某个资源文件】
struts2为我们提供了<s:i18n>标签,使用<s:i18n>标签我们可以在类路径下直接从某个资源
文件中获取国际化数据,而无需任何配置:
<s:i18n name="itcast">
<s:text name="welcome"/>
</s:i18n>
itcast为类路径下资源文件的基本名。
如果要访问的资源文件在类路径的某个包下,可以这样访问:
<s:i18n name="cn/itcast/action/package">
<s:text name="welcome">
<s:param>小张</s:param>
</s:text>
</s:i18n>
上面访问cn.itcast.action包下基本名为package的资源文件。
【动态国际化页】
通过页面上的选项来选择页面显示语言,
通过参数request_local来设定,我们在请求url后加上该参数也可以。
url?request_local=en_US
【OGNL表达式】
OGNL是Object Graphic Navigation Language(对象图导航语言)的缩写,
它是一个开源项目。 Struts 2框架使用OGNL作为默认的表达式语言。
相对EL表达式,它提供了平时我们需要的一些功能,如:
支持对象方法调用(也支持对象级联调用),如xxx.sayHello();
<s:property value="password.length()" /> 调用属性对应的方法
<s:property value="execute()" /> 调用Action类中的普通方法
支持类静态方法调用和值访问,
表达式的格式为 @[类全名(包括包路径)]@[方法名 | 值名],
例如:@java.lang.String@format('foo %s', 'bar')或@cn.csdn.Constant@APP_NAME;
@@mxa(2,3)只支持访问Math类中的静态方法
支持普通类的构造方法的调用:<s:property value="new cn.itcase.bean.User()"/>
操作集合对象。
Ognl 有一个上下文(Context)概念,说白了上下文就是一个MAP结构,它实现了java.utils.Map接口,在Struts2中上下文(Context)的实现为ActionContext,下面是上下文(Context)的结构示意图
|--ValueStack(值栈,这是根对象)
|--contextparameters
OGNL Context----|--request
|--session
|--application
|--attr
Struts2中的OGNL Context实现者为ActionContext
当Struts2接受一个请求时,会迅速创建ActionContext,ValueStack,action。然后把action存进
ValueStack,所以action的实例变量可以被OGNL访问
访问上下文(Context)中的对象需要使用#符号标注命名空间,如#application、#session
另外OGNL会设定一个根对象(root对象),在Struts2中根对象就是ValueStack(值栈) 。
如果要访问根对象(即ValueStack)中对象的属性,则可以省略#命名空间,直接访问该对象的属性即可。
在struts2中,根对象ValueStack的实现类为OgnlValueStack,该对象不是我们想像的只存放单个值,而是存放一组对象。在OgnlValueStack类里有一个List类型的root变量,就是使用他存放一组对象
|--request
|--application
context --------|--OgnlValueStack root变量[action, OgnlUtil, ... ]
|--session
|--attr
|--parameters
在root变量中处于第一位的对象叫栈顶对象。通常我们在OGNL表达式里直接写上属性的名称
即可访问root变量里对象的属性,搜索顺序是从栈顶对象开始寻找,如果栈顶对象不存在该
属性,就会从第二个对象寻找,如果没有找到就从第三个对象寻找,依次往下访问,直到找到为止。
注意:Struts2中,OGNL表达式需要配合Struts标签才可以使用。如:<s:property value="name"/>
由于ValueStack(值栈)是Struts 2中OGNL的根对象,如果用户需要访问值栈中的对象,
在JSP页面可以直接通过下面的EL表达式访问ValueStack(值栈)中对象的属性:
${foo} //获得值栈中某个对象的foo属性
如果访问其他Context中的对象,由于他们不是根对象,所以在访问时,需要添加#前缀。
application对象:用于访问ServletContext,例如#application.userName或者#application['userName'],相当于调用ServletContext的getAttribute("username")。
session对象:用来访问HttpSession,例如#session.userName或者#session['userName'],相当于调用session.getAttribute("userName")。
request对象:用来访问HttpServletRequest属性(attribute)的Map,例如#request.userName或者#request['userName'],相当于调用request.getAttribute("userName")。
parameters对象:用于访问HTTP的请求参数,例如#parameters.userName或者#parameters['userName'],相当于调用request.getParameter("username")。
attr对象:用于按page->request->session->application顺序访问其属性。
【为何使用EL表达式能够访问valueStack中对象的属性】
原因是Struts2对HttpServletRequest作了进一步的封装。简略代码如下:
public class StrutsRequestWrapper extends HttpServletRequestWrapper {
public StrutsRequestWrapper(HttpServletRequest req) {
super(req);
}
public Object getAttribute(String s) {
......
ActionContext ctx = ActionContext.getContext();
Object attribute = super.getAttribute(s);//先从request范围获取属性值
if (ctx != null) {
if (attribute == null) {//如果从request范围没有找到属性值,即从ValueStack中查找对象的属性值
......
ValueStack stack = ctx.getValueStack();
attribute = stack.findValue(s);
......
}
}
return attribute;
}
}
【采用OGNL表达式创建List/Map集合对象】
如果需要一个集合元素的时候(例如List对象或者Map对象),可以使用OGNL中同集合相关的表达式。
使用如下代码直接生成一个List对象:
<s:set name="list" value="{'zhangming','xiaoi','liming'}" />
<s:iterator value="#list" id="n">
<!--value:赋给变量的值.如果没有设置该属性,则将ValueStack栈顶的值赋给变量。-->
<s:property value="n"/><br>
</s:iterator>
生成一个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>
<s:property value="#foobar.keys" /> 获取Map中的所有key
<s:property value="#foobar.values" /> 获取Map中的所有Value
<s:property value="#foobar.size()" /> 获取Map的大小
Set标签用于将某个值放入指定范围。
scope:指定变量被放置的范围,该属性可以接受application、session、request、 page或action。
如果没有设置该属性,则默认放置在OGNL Context中,单独成为一个作用范围。
<s:iterator>的特点:会把当前迭代的对象放在ValueStack栈顶
<s:property value="[0]" />功能:访问ValueStack中从第1个开始的之后所有的对象
<s:property value="[1]" />功能:访问ValueStack中从第2个开始的之后所有的对象
【采用OGNL判断对象是否在集合中】
通过: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="books.{?#this.price > 35}"> //用于获取集合中价格大于35的书集合。
<!--title price都是Book类的属性-->
<s:property value="title" /> - $<s:property value="price" /><br>
</s:iterator>
在上面代码中,直接在集合后紧跟.{}运算符表明用于取出该集合的子集,
{}内的表达式用于获取符合条件的元素,this指的是为了从大集合books筛选数据到小集合public class BookAction extends ActionSupport {
private List<Book> books;
....
@Override
public String execute() {
books = new LinkedList<Book>();
books.add(new Book("A735619678", "spring", 67));
books.add(new Book("B435555322", "ejb3.0",15));
}
}
【Struts2常用标签】
==============================================================================================
<s:set>标签
Set标签用于将某个值放入指定范围。
scope:指定变量被放置的范围,该属性可以接受application、session、request、 page或action。
默认值为action,如果没有设置该属性,则默认放置在OGNL Context中,单独成为一个作用范围。
var:该属性是可选的,所要参考的ValueStack中的值的名称。指定该属性时,才会被放入OGNL context
id,name属性已经标注为过时
例如:
<s:set var="adminPassword" value="password" />
取的时候:
<s:property value="#adminPassword">
例如:
<s:set var="adminPassword" value="password" scope="session" />
取的时候:
<s:property value="#session.adminPassword">
==============================================================================================
<s:property>标签
property标签的作用是输出指定值。
<s:set name="name" value="kk" />
<s:property value="#name" />
default:可选属性,如果需要输出的属性值为null,则显示该属性指定的值
escape:可选属性,指定是否格式化HTML代码,默认为true,不解析HTML代码,false时以HTML代码来解析
value:可选属性,指定需要输出的属性值,如果没有指定该属性,则默认输出值栈ValueStack栈顶的值。
id:可选属性,指定该元素的标识
==============================================================================================
<s:bean>标签
作用:实例化JavaBeans
var:该属性是可选的,所要参考的ValueStack中的值的名称。指定该属性时,才会被放入OGNL context
name:要被实例化的JavaBean类的完整名称
<s:bean name="cn.itcast.bean.Book" var="book">
<s:param name="name" value="'javaEE'"></s:param>
<s:property value="name" /> 在bean内部可以这样使用
</s:bean>
在标签内,实例出的bean对象会被保存到值栈栈顶,还有OGNL context中(前提指定var),
标签结束后栈顶的对象会弹出。
所以在标签内部<s:property value="name" />可以取出值
在外面就需要:<s:property value="#book.name" />
<s:param>标签
作用:给其它标签注入参数
name:参考目标的属性名
value:OGNL表达式,如果要写字符串,需要加单引号
==============================================================================================
<s:iterator>标签
iterator标签用于对集合进行迭代,这里的集合包含List、Set和数组。
也可以对Map类型的对象进行迭代输出。
<s:set name="list" value="{"张三","李四","王五"}" />
<s:iterator value="#list" status="st">
<font color=<s:if test="#st.odd">red</s:if><s:else>blue</s:else>>
<s:property />
</font>
<br/>
</s:iterator>
value:可选属性,指定被迭代的集合,被迭代的集合通常都由OGNL表达式指定。
如果没有指定该属性,则使用值栈栈顶的集合。
id:可选属性,指定集合里元素的id(已被标注为过时)
status:可选属性,指定迭代时的IteratorStatus实例,通过该实例可判断当前迭代元素的属性。该实例包含如下几个方法:
int getCount():返回当前迭代了几个元素
int getIndex():返回当前迭代元素的索引
boolean isEven():返回当前迭代元素的索引是否是偶数
boolean isOdd():返回当前迭代元素的索引是否是奇数
boolean isFirst():返回当前迭代元素的索引是否是第一个元素
boolean isLast():返回当前迭代元素的索引是否是最后一个元素
例如:
<table border="1" width="200">
<s:iterator value="{'apple','orange','pear','banana'}" id="fruit" status="st">
<tr <s:if test="#st.even">style="background-color:silver"</s:if>>
<td>
<s:property value="fruit"/>
</td>
</tr>
</s:iterator>
</table>
==============================================================================================
<s:if>/<s:elseif>/<s:else>标签
只有if标签可以单独使用,if标签可以与多个elseif标签结合使用,但只能与一个else标签使用。其用法格式如下:
<s:if test="表达式">
?标签体
</s:if>
<s:elseif test="表达式">
标签体
</s:elseif>
<!--允许出现多次elseif标签-->
...
<s:else>
标签体
</s:else>
==============================================================================================
<s:url>标签
<s:url action="helloworld_add" namespace="/test">
<s:param name="personId" value="23" />
</s:url>
生成类似如下的路径:
contextName/test/helloworld_add.action?personId=23
<s:param>中value的值可以是OGNL表达式,当表达式无法解析时会原样输出
<s:url>标签的value属性是作为字符串类型处理的(所写的OGNL表达式会原样输出),
"%{ognl}"符号用来计算OGNL表达式的值:
<s:set name="myurl" value="http://www.baidu.com" />
<s:url value="#myurl" />
<s:url value="%{#myurl}" />
输出结果:
#myurl
http://www.baidu.com
一般与<a>标签结合使用:
<a href="<s:url namespace='' action='' />">一个链接</a>
链接会自动生成 当前应用名称、请求后缀
==============================================================================================
<s:param>标签
param标签主要用于为其他标签提供参数,该标签有如下几个属性:
name:该属性是可选的,指定需要设置参数的参数名。
value:该属性是可选的,指定需要设置参数的参数值。
id:该属性是可选的,指定引用该元素的id。
==============================================================================================
<s:checkboxlist>标签
为List:
<s:checkboxlist label="请选择你喜欢的水果" list="{'apple','oranger','pear','banana'}" name="fruit" value="{'apple','pear'}">
</s:checkboxlist>
name属性:生成复选框的名称
list属性:list集合,迭代集合生成复选框,若当前迭代值在value中标明,则生成checked属性
value属性:指定被checked的复选框
生成如下的HTML标签:
<input type="checkbox" name="fruit" value="apple" checked="checked" /><label>apple</label>
<input type="checkbox" name="fruit" value="oranger" /><label>oranger</label>
<input type="checkbox" name="fruit" value="pear" checked="checked" /><label>pear</label>
<input type="checkbox" name="fruit" value="banana" /><label>banana</label>
为Map:
<s:checkboxlist label="请选择你喜欢的水果" list="#{1:'apple',2:'oranger',3:'pear',4:'banana'}" name="fruit" value="{1,3}" listKey="key" listValue="value">
</s:checkboxlist>
listKey="key":指定Map的key为复选框中的value值,默认值可不写
listValue="value":指定Map的value为复选框外label标签对之间的值,默认值可不写
生成如下的HTML标签:
<input type="checkbox" name="fruit" value="1" checked="checked" /><label>apple</label>
<input type="checkbox" name="fruit" value="2" /><label>oranger</label>
<input type="checkbox" name="fruit" value="3" checked="checked" /><label>pear</label>
<input type="checkbox" name="fruit" value="4" /><label>banana</label>
【注意】使用<s:checkboxlist>标签,除了生成相应的复选框标签外,还会生成一些<td>标签,
因为Struts还有视图主题设置,我们可以取消主题设置,这样就不会生成多余的<td>标签:
设置常量:
<constant name="struts.ui.theme" value="simple">
集合中放的是javabean,Person类有两个属性:personid,name
<%
Person p1 = new Person(1,"第一个");
Person p2 = new Person(2,"第二个");
List<Person> list = new ArrayList<Person>();
list.add(p1);
list.add(p2);
request.setAttribute("persons",list);
%>
<s:checkboxlist name="beans" list="#request.persons" listKey="personid" listValue="name" />
生成的HTML代码如下:
<input type="checkbox" name="beans" value="1" /><label>第一个<label>
<input type="checkbox" name="beans" value="2" /><label>第二个<label>
==============================================================================================
<s:radio>标签
<s:radio>与<s:checkboxlist>使用几乎一样
不同的是value属性的设置:不需要再用{},单选框嘛,只有一个预选值
<s:radio label="性别" list="{'男','女'}" name="sex" value="'男'" />
或者为Map:
<s:radio label="性别" list="#{1:'男',0:'女'}" name="sex" value="1" />
或者为javabean:
<s:radio name="beans" list="#request.books" listKey="id" listValue="name" />
==============================================================================================
<s:select>标签
与<s:radio>使用一样
<s:select name="fruit" list="{'apple','oranger','pear','banana'}" label="请选择你喜欢的水果"/>
或者为Map:
<s:select name="fruit" list="#{1:'apple',2:'oranger',3:'pear',4:'banana'}" listKey="key" listValue="value"/>
或者为javabean:
<s:select name="beans" list="#request.books" listKey="id" listValue="name" />
==============================================================================================
<s:action>标签
使用action标签可以允许在JSP页面中直接调用Action。
name:该属性是必选的,指定该标签调用哪个Action。
==============================================================================================
<s:date>标签
date标签主要用于格式化输出一个日期。该标签有如下属性:
format:该属性是可选的,如果指定了该属性,将根据该属性指定的格式来格式化日期。
nice:该属性是可选的,指定是否输出指定日期和当前时刻之间的时差。默认为false。
name:属性是必选的,指定要格式化的日期值。
其用法为:
<s:date name="指定日期取值" format="日期格式"/><!-- 按指定日期格式输出 -->
<s:date name="指定日期取值" nice="true"/><!-- 输出时间差 -->
<s:date name="指定日期取值"/><!—默认格式输出-->
==============================================================================================
<s:include>标签
include标签用于将一个JSP页面或一个Servlet包含到本页面中。
value:该属性是必选的,指定需要被包含的JSP页面或Servlet。
例如:<s:include value="JSP或Servlet文件" id="自定义名称"/>
==============================================================================================
<s:append>标签
例如:<body>
<s:append id="newList">
<s:param value="{'apple','orange','pear','banana'}"/>
<s:param value="{'chinese','english','french'}"/>
</s:append>
<table border="1" width="200">
<s:iterator value="#newList" id="fruit" status="st">
<tr <s:if test="#st.even">style="background-color:silver"</s:if>>
<td><s:property value="fruit"/></td>
</tr>
</s:iterator>
</table>
</body>
==============================================================================================
<s:merge>标签
与append标签作用相似,只是它们所拼接产生的新集合元素顺序不同,其中merge是每次分别在每一个集合取一个元素进行拼接。
==============================================================================================
<s:combobox>标签
<s:combobox label="请选择你喜欢的水果" list="{'apple','oranger','pear','banana'}" name="fruit">
</s:combobox>
==============================================================================================
<s:datetimepicker>标签
<s:form action="" method="">
?<s:datetimepicker name="date" label="请选择日期"></s:datetimepicker>
</s:form>
==============================================================================================
【<s:token/>标签防止重复提交】
第一步:在表单中加入<s:token />
<s:form action="helloworld_other" method="post" namespace="/test">
<s:textfield name="person.name"/>
<s:token />
<s:submit />
</s:form>
第二步:给Action添加token拦截器、及invalid.token的视图
<action name="helloworld_*" class="cn.itcast.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拦截器在会话的token与请求的token不一致时,将会直接返回invalid.token结果
token拦截器是Struts2自带的,不需要编写
【通过url传输List、Map类型的数据】
以http://localhost:8080/project/login.action为例
传输一个List<String> interests类型的数据
http://localhost:8080/project/login.action?interests=programming&interests=sport&interests=sleep
传输一个Map<String, String> users类型的数据
http://localhost:8080/project/login.action?users['a']=usera&users['b']=userb