软件:Myeclipse 8.6
Struts2是在WebWork2基础发展而来的。和struts1一样, Struts2也属于MVC框架。
不过有一点大家需要注意的是:尽管Struts2和struts1在名字上的差别不是很大,但Struts2和struts1在代码编写风格上几乎是不一样的。
找到开发Struts2应用需要使用到的jar文件
现在struts2没与spring集成,所以不需要导入struts2-spring-plugin-2.1.8.jar
编写Struts2的配置文件struts.xml,该文件需要存放在src下或者WEB-INF/classes下
方式一:右击项目–>MyEclipse–>Add… (注意:在web.xml会自动配置struts2,访问时以.action结束)
使用这种方式就不需要上一步(不需要加入jar文件)
方式二:拷贝下面文件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1//EN" "http://struts.apache.org/dtds/struts-2.1.dtd"> <struts> <!-- name 包名,唯一 namespace 访问路径 extends 必须继承struts-default --> <package name="user" namespace="/user" extends="struts-default"> <action name="userAction" class="action.UserAction"></action> </package> </struts>
创建一个Action,需要继承ActionSupport,实现execute方法
<filter> <filter-name>struts</filter-name> <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class></filter> <filter-mapping> <filter-name>struts</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
默认值
action的class默认访问ActionSupport
action的method默认为execute()
result的type默认为success
http://localhost:8080/项目名/包的命名空间/Action名称.action
<result name="redi" type="redirectAction">testAction</result> <!--重定向到第二个包(/path1)下的action(loginAction)中--> <result name="redi" type="redirectAction"> <param name="actionName">loginAction</param> <param name="namespace">/path1</param> </result>
3. plainText 显示jsp源码
<result name="plain" type="plainText"> <param name="location">/index.jsp</param> <param name="charSet">UTF-8</param> </result>
Struts2为Action中的属性提供了依赖注入功能,在struts2的配置文件中,我们可以很方便地为Action中的属性注入值。注意:属性必须提供setter方法。
<action name="helloworld" class="com.jyp.action.LoginAction"> <param name="savePath">/images</param> <result name="success">/helloworld.jsp</result> </action>
上面通过节点为action的savePath属性注入“/images”
默认使用.action后缀访问Action。其实默认后缀是可以通过常量”struts.action.extension“进行修改的。
<struts> <constant name="struts.action.extension" value="do"></constant> </struts>
如果用户需要指定多个请求后缀,则多个后缀之间以英文逗号(,)隔开。
<struts> <constant name="struts.action.extension" value="do,action"></constant> </struts>
1、常量可以在struts.xml或struts.properties中配置,建议在struts.xml中配置;
两种配置方式如下:
在struts.xml文件中配置常量
<struts> <constant name="struts.action.extension" value="do"/> </struts> <!--在struts.properties中配置常量 struts.action.extension=do-->
2、struts2加载常量的搜索顺序:
struts-default.xml
struts-plugin.xml
struts.xml
struts.properties
web.xml
如果在多个文件中配置了同一个常量,则后一个文件中配置的常量值会覆盖前面文件中配置的常量值.
<!--指定默认编码集,作用于HttpServletRequest的setCharacterEncoding方法 和freemarker 、velocity的输出 --> <constant name="struts.i18n.encoding" value="UTF-8"/> <!-- 该属性指定需要Struts 2处理的请求后缀,该属性的默认值是action,即所有匹配*.action的请求都由Struts2处理。如果用户需要指定多个请求后缀,则多个后缀之间以英文逗号(,)隔开。 --> <constant name="struts.action.extension" value="do"/> <!-- 设置浏览器是否缓存静态内容,默认值为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" /> <!--该属性设置Struts 2是否支持动态方法调用,该属性的默认值是true。如果需要关闭动态方法调用,则可设置该属性为false。--> <constant name="struts.enable.DynamicMethodInvocation" value="false"/> <!--上传文件的大小限制 单位:字节 21000000 (约21M) --> <constant name="struts.multipart.maxSize" value=“10701096"/>
为了避免struts.xml文件过于庞大、臃肿,提高struts.xml文件的可读性,我们可以将一个struts.xml配置文件分解成多个配置文件,然后在struts.xml文件中包含其他配置文件。
下面的struts.xml通过元素指定多个配置文件:
<struts> <include file="struts-user.xml"></include> <include file="struts-manager.xml"></include> </struts>
如果Action中存在多个方法时,我们可以使用!+方法名调用指定方法
调用addUser对应action中的add方法
http://localhost:8080/Struts2_1/user/addUser!add.do
如果不想使用动态方法调用,我们可以通过常量struts.enable.DynamicMethodInvocation关闭动态方法调用。
<constant name="struts.enable.DynamicMethodInvocation" value="false"></constant>
<action name="addUser_*" class="com.jyp.action.UserAction" method="{1}"> <!--URL访问:http://localhost:8080/Struts2_1/user/addUser_add.do-->
<action name="loginAction" method="login" class="action.loginAction"> <result name="success">/list.jsp</result> </action>
在Action类中定义与请求参数同名的属性,struts2便能自动接收请求参数并赋予给同名属性。
public void setUser_id(int userId) {//struts2通过反射技术调用与请求参数同名的属性的setter方法来获取请求参数值
user_id = userId;
Struts2首先通过反射技术调用Users的默认构造器创建User对象,然后再通过反射技术调用User中与请求参数同名的属性的setter方法来获取请求参数值。
a、表单元素name="users.user_name"
b、显示页面${users.user_name}
java.util.Date类型的属性可以接收格式为2009-07-20的请求参数值。但如果我们需要接收格式为20091221的请求参数,我们必须定义类型转换器。
定义一个类继承DefaultTypeConverter,并实现convertValue方法
public class DateConverter extends DefaultTypeConverter { @Override public Object convertValue(Map<String, Object> context, Object value, Class toType) { SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd"); try { if (toType == Date.class) { String[] params = (String[]) value; return dateFormat.parse(params[0]); }else if(toType==String.class){ Date date=(Date)value; return dateFormat.format(date); } } catch (ParseException e) {} return null; } }
在Action类所在的包下放置ActionClassName-conversion.properties
文件,ActionClassName是Action的类名,后面的-conversion.properties是固定写法。
在properties文件中的内容为:属性名称=类型转换器的全类名user_date=com.jyp.convertion.DateConverter
如果action中是复合类型,则写
user.user_date=com.jyp.convertion.DateConverter
注意:属性名如user_date中不能存在大写字母,否则转换不成功,输出null
将上面的类型转换器注册为全局类型转换器
1、在WEB-INF/classes或src根目录下放置xwork-conversion.properties文件 。
2、在properties文件中的内容为:
待转换的类型=类型转换器的全类名xwork-conversion.properties文件中的内容为:
java.util.Date=com.jyp.convertion.DateConverter
可以用表单或超链接测试(使用超链接测试时日期字符串不需要’引号):
<a href="official/official_add.action?mydate=20121123">测试类型转换器</a>
public class MeetingAction extends ActionSupport { private User user; getter|setter方法... }
jsp页面
定义了类型转换器,使用user和user.id都可以
用户ID:
用户名:
不用自定义类型转换器的话,上面必须写成name="user.id"和name=“user.name”
PageContext:
页面上下文[页面级容器,在本页面绑定的数据只能在本页面使用]
request:
请求:只能在转发的目的地获取数据
例如: 从A发送请求到B,B没有选择转发,此请求销毁
请求是一次性请求,每一次发送一个请求都是一个新的请求的对象
session:
会话:绑定在session中的数据,任何地方默认30分钟内可以获取数据
绑定一次数据,30分钟,任何位置都可以得到数据
一般用来保存当前登陆者的信息[每一种浏览器都有自己独有的session]
application:
项目上下文
[项目级容器:绑定在此容器中的数据,项目不停止,任何地方都可以获取]
绑定一次,任何地方任何浏览器都可以取到数据
public String scopeParam() throws Exception { ActionContext ctx=ActionContext.getContext(); //Map<String, Object> session = ActionContext.getContext().getSession(); //HttpServletRequest request = ServletActionContext.getRequest(); //HttpServletResponse response = ServletActionContext.getResponse(); ctx.put("req","请求"); ctx.getSession().put("username","玛丽"); ctx.getApplication().put("password","123456"); ctx.put("age",18); return "success"; }
在WEB-INF/lib下加入commons-fileupload-1.2.1.jar、commons-io-1.3.2.jar
把form表的enctype设置为:“multipart/form-data”
<form enctype="multipart/form-data" action="${pageContext.request.contextPath}/upload/uploadimage.action" method="post"> <input type="file" name="uploadImage"/><br/> <input type="submit" value="上传"/> </form>
在Action类中添加以下属性,属性前部分对应于表单中文件字段的名称
private File uploadImage;//与表单元素名一致 private String uploadImageFileName;//文件名--前部分与表单元素名一致 private String uploadImageContentType;//文件类型--前部分与表单元素名一致 getter/setter方法 public String execute() throws Exception { String realPath=ServletActionContext.getServletContext().getRealPath("/images"); File file=new File(realPath); if(!file.exists()){ file.mkdir(); } FileUtils.copyFile(uploadImage,new File(file,uploadImageFileName)); return "success"; }
public class UploadImageAction extends ActionSupport { private File uploadImage;//与表单元素名一致 private String uploadImageFileName;//文件名--前部分与表单元素名一致 private String uploadImageContentType;//文件类型--前部分与表单元素名一致 getter/setter方法 public String execute() throws Exception { String realPath=ServletActionContext.getServletContext().getRealPath("/images"); File file=new File(realPath); if(!file.exists()){ file.mkdir(); } FileUtils.copyFile(uploadImage,new File(file,uploadImageFileName)); return "success"; } }
在jsp页面多加几个File元素,把action中的属性改成数组,上传方法使用循环一个一个上传
异常信息: Unable to find ‘struts.multipart.saveDir’ property setting
解决方案:
struts.xml中添加常量
<constant name="struts.multipart.saveDir" value="/tmp"></constant>
jsp页面:
<form enctype="multipart/form-data" action="${pageContext.request.contextPath }/upload/uploadimage.action" method="post"> <input type="file" name="uploadImage"/><br/> <input type="file" name="uploadImage"/><br/> <input type="file" name="uploadImage"/><br/> <input type="submit" value="上传"/> </form>
Action文件:
public class UploadImageAction extends ActionSupport { private File[] uploadImage;//与表单元素名一致 private String[] uploadImageFileName;//文件名--前部分与表单元素名一致 private String[] uploadImageContentType;//文件类型--前部分与表单元素名一致 getter/setter方法 public String execute() throws Exception { String realPath=ServletActionContext.getServletContext().getRealPath("/images"); File file=new File(realPath); if(!file.exists()){ file.mkdirs();//多文件上传,这里调用带s的方法 } for(int i=0;i<uploadImage.length;i++){ FileUtils.copyFile(uploadImage[i],new File(file,uploadImageFileName[i])); } return "success"; } }
实现com.opensymphony.xwork2.interceptor.Interceptor接口
public String intercept(ActionInvocation invocation) throws Exception { String result; Object obj=ActionContext.getContext().getSession().get("user"); if(obj!=null){//session里存在用户 result=invocation.invoke(); }else{ result="logon"; } return result; }
配置struts.xml
<package name="user" namespace="/user" extends="struts-default"> <interceptors> <interceptor name="permission" class="com.jyp.aop.PermissionInterceptor"/> <interceptor-stack name="permissionStack"> <interceptor-ref name="defaultStack"/> <interceptor-ref name="permission"/> </interceptor-stack> </interceptors> <action name="shopping" class="com.jyp.action.ShoppingAction" method="execute"> <result name="goShopping">/WEB-INF/page/goShopping.jsp</result> <interceptor-ref name="permissionStack"></interceptor-ref> </action> </package>
因为struts2中如文件上传,数据验证,封装请求参数到action等功能都是由系统默认的defaultStack中的拦截器实现的;
所以我们定义的拦截器需要引用系统默认的defaultStack,这样应用才可以使用struts2框架提供的众多功能。
<!-- 包中指定默认拦截器需放在interceptors以下 --> <default-interceptor-ref name="permissionStack"></default-interceptor-ref>
包下的所有action都使用自定义的拦截器:
<interceptors> <interceptor name="permission" class="com.jyp.aop.PermissionInterceptor"/> <interceptor-stack name="permissionStack"> <interceptor-ref name="defaultStack"/> <interceptor-ref name="permission"/> </interceptor-stack> </interceptors> <!-- 包中指定默认拦截器需放在interceptors以下,注意是interceptor-stack的名字 --> <default-interceptor-ref name="permissionStack"></default-interceptor-ref>
在struts2中,我们可以实现对action的所有方法进行校验或者对action的指定方法进行校验。
对于输入校验struts2提供了两种实现方法:
新建CustomerAction,继承ActionSupport,添加jsp页面,修改配置文件(不能是*.action,要改成/*)。
<filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping></web-app>
调用addFieldError()方法往系统的fieldErrors添加校验失败信息,action需要继承ActionSupport。
@Override public void validate() { if(this.phone==null || "".equals(this.phone.trim())){ this.addFieldError("phone","手机号不能为空"); }else if(!Pattern.compile("^1[358]\\d{9}").matcher(this.phone).matches()){ this.addFieldError("phone","手机号格式不对"); } }
fieldErrors包含失败信息,struts2会将请求转发到【名为input的result】。
<%@ taglib uri="/struts-tags" prefix="s"%><!--导入struts标签--> <s:fielderror/>显示失败信息。 <s:fielderror fieldName="phonemsg">\</s:fielderror>显示指定的某个元素
使用基于XML配置方式实现输入校验时,Action也需要继承ActionSupport,并且提供校验文件,校验文件和action类放在同一个包下;
文件的取名格式为:ActionClassName-validation.xml,其中ActionClassName为action的简单类名,-validation为固定写法。
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0.3//EN" "http://www.opensymphony.com/xwork/xwork-validator-1.0.3.dtd"> <validators> <field name="username"> <field-validator type="requiredstring"> <param name="trim">true</param> <message>用户名不能为空!</message> </field-validator> </field> <!-- 方式一:使用字段校验器格式 --> <field name="phone"> <field-validator type="regex"> 注意2.3.15前的版本使用expression参数 之后的使用regexExpression <param name="expression"><![CDATA[1[348]\d{9}]]></param> <message>Tel格式不正确</message> </field-validator> </field> --> <!-- 方式二:使用非字段校验器格式:用来配置正则表达式校验器 --> <validator type="regex"> <!-- 指定校验字段:phone --> <param name="fieldName">phone</param> <!-- 指定匹配的正则表达式 --> <param name="expression"><![CDATA[1[348]\d{9}]]></param> <!-- 指定校验失败的提示信息 --> <message>手机号码格式不正确</message> </validator> </validators> 补充 <field name="repassword"> <field-validator type="fieldexpression"> <param name="expression"><![CDATA[repassword==password]]></param> <message>二次密码不一致</message> </field-validator> </field>
windwos->preferences->myeclipse->files and editors->xml->xmlcatalog
点“add”,
在出现的窗口中的location中选“File system”,
然后在xwork-2.1.2解压目录的src\java目录中选择xwork-validator-1.0.3.dtd,
回到设置窗口的时候不要急着关闭窗口,应把窗口中的Key Type改为URI 。
Key改为http://www.opensymphony.com/xwork/xwork-validator-1.0.3.dtd
注意:ActionName是struts.xml中<action name="userAction_"的name的值。
当为某个action提供了ActionClassName-validation.xml和ActionClassName-ActionName-validation.xml两种规则的校验文件时,系统按下面顺序寻找校验文件:
注意:
1、如果是复合类型接收请求参数:使用对象名.属性即可
2、一个属性可以跟多个来验证。
准备资源文件,资源文件的命名格式如下:baseName_language_country.properties
baseName_language.properties
baseName.properties其中baseName是资源文件的基本名,我们可以自定义,但language和country必须是java支持的语言和国家。
如:
中国大陆: baseName_zh_CN.properties
美国: baseName_en_US.properties
对于中文的属性文件,我们编写好后,应该使用jdk提供的native2ascii命令把文件转换为unicode编码的文件。
添加两个资源文件:
a、jyp_zh_CN.properties
welcome=欢迎来到中国
b、jyp_en_US.properties
welcome=welcome to china
配置全局资源与输出国际化信息
1、在struts.xml中通过struts.custom.i18n.resources常量把资源文件定义为全局资源文件,如下:
2、在页面或在action中访问国际化信息:
(1)、在JSP页面中使用<s:text name=""/>标签输出国际化信息:
<s:text name=“welcome”/>,name为资源文件中的key
(2)、在Action类中,可以继承ActionSupport,使用getText()方法得到国际化信息,该方法的第一个参数用于指定资源文件中的key。
在表单标签中,通过key属性指定资源文件中的key,如:
<s:textfield name="realname" key="user"/>
修改资源文件
welcome 欢迎{0}来到中国–{1}
修改页面或action中的方法
<s:text name="welcome"> <s:param value="realname"></s:param> <s:param>IT精英</s:param> </s:text>
或者
getText(String key, String[] args) //或 getText(String aTextName, List args)//方法 ActionContext.getContext().put("message",this.getText("welcome",new String[]{"小李子","中国IT"}));
注意:上面的realname是action的属性,请求时传参过来即可,如下。
输入下面网址测试jsp页面显示带占位符的国际化信息
http://localhost:8080/Struts2_7/person/personManager_executeJSPByParam.action?realname=MaLili
把国际化的内容都放置在全局资源属性文件中,会导致资源文件变的过于庞大、臃肿,不便于维护,这个时候我们可以针对不同模块,使用包范围来组织国际化文件。
在java的包下放置package_language_country.properties资源文件;
注意:例如package_zh_CN.properties,直接写package就行,不要写成真正的包啦
package为固定写法,处于该包及子包下的action都可以访问该资源;
当查找指定key的消息时,系统会先从package资源文件查找,当找不到对应的key时,才会从常量struts.custom.i18n.resources指定的资源文件中寻找。
在Action类所在的路径,放置ActionClassName_language_country.properties资源文件,ActionClassName为action类的简单名称。
如:AuthorAction_zh_CN.properties
当查找指定key的消息时,系统会先从ActionClassName_language_country.properties资源文件查找;
如果没有找到对应的key,然后沿着当前包往上查找基本名为package 的资源文件,一直找到最顶层包;
如果还没有找到对应的key,最后会从常量struts.custom.i18n.resources指定的资源文件中寻找。
struts2为我们提供了<s:i18n>标签,使用<s:i18n>标签我们可以在类路径下直接从某个资源文件中获取国际化数据,而无需任何配置。
jyp为类路径下资源文件的基本名。
注意:类路径下只能有jyp_zh_CN.properties文件,不能有其它属性文件
<s:i18n name="jyp"> <s:text name="welcome"> <s:param>凤姐</s:param> <s:param>(^_^)</s:param> </s:text> </s:i18n>
要访问的资源文件在类路径的某个包下,如属性名
package_zh_CN.properties
<s:i18n name="com/jyp/action1/package"> <s:text name="authorname"></s:text> </s:i18n>
上面访问com.jyp.action1包下基本名为package的资源文件。
要访问的资源文件在类路径的Action下,下面NewAction为action的类名
<s:i18n name="com/huarui/action1/NewAction"> <s:text name="username"></s:text> </s:i18n>
OGNL是Object Graphic Navigation Language(对象图导航语言)的缩写,它是一个开源项目。 Struts 2框架使用OGNL作为默认的表达式语言。
相对EL表达式,它提供了平时我们需要的一些功能:
a、支持对象方法调用,如xxx.sayHello();
b、支持类静态方法调用和值访问,表达式的格式为@[类全名(包括包路径)]@[方法名 | 值名],
例如:@java.lang.String@format(‘foo %s’, ‘bar’)或@cn.itcast.Constant@APP_NAME;
c、
操作集合对象。
示例
action中的属性:
private Student student=new Student(); public static double PI=3.1415926; public static String mm(){ return "mm方法"; }
jsp页面:
<s:property value="student.toString()"/> <s:property value="@com.huarui.domain.Student@mm()"/><br/> <s:property value="@com.huarui.domain.Student@PI"/>
总结:对于使用OGNL表达式语言来访问静态方法比较少用,如果获取不到值,是版本不稳定的原因,要换个版本。
不能访问静态方法:
解决方案:原来是在2.1.2中,如果要通过ognl访问静态方法,必须在struts.properties或者struts.xml中将选项struts.ognl.allowStaticMethodAccess设置为true
设置常量
<constant name="struts.ognl.allowStaticMethodAccess" value="true"></constant>
|--- ValueStack(值栈,它是根对象) |--- parameters |--- request OGNL Context ---|--- session |--- application |--- attr
当Struts2接受一个请求时,会迅速创建ActionContext,ValueStack,action 。
然后把action存放进ValueStack,所以action的实例变量可以被OGNL访问。
OGNL表达式语言
a、访问上下文(Context)中的对象需要使用#符号标注命名空间,如#application、#session
b、OGNL会设定一个根对象(root对象),在Struts2中根对象就是ValueStack(值栈)。可以省略#命名空间,直接访问该对象的属性即可。
c、在struts2中,根对象ValueStack的实现类为OgnlValueStack,该对象不是我们想像的只存放单个值,而是存放一组对象。
d、在root变量中处于第一位的对象叫栈顶对象。通常我们在OGNL表达式里直接写上属性的名称即可访问root变量里对象的属性;
搜索顺序是从栈顶对象开始寻找,如果栈顶对象不存在该属性,就会从第二个对象寻找,如果没有找到就从第三个对象寻找,依次往下访问,直到找到为止。
大家注意: Struts2中,OGNL表达式需要配合Struts标签才可以使用。如:<s:property value=“name”/>
${foo} //获得值栈中某个对象的foo属性
b、如果访问其他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”)。
a.jsp页面
<form method="post" action="b.jsp"> <input type="text" name="username"/><input type="submit" value="提交"/> </form>
b.jsp页面
<s:property value="#parameters.username"/>
原因是Struts2对HttpServletRequest作了进一步的封装。
生成一个List对象
<s:set name="names" value="{'张三','李四','王五'}"></s:set> <s:iterator value="#names" id="n"> <s:property value="n"/><br/> </s:iterator>
生成一个Map对象
<!--生成一个Map对象,注意value前有一个#号 --> <s:set name="customers" value="#{'1':'李总','2':'王总','3':'蒋总'}"></s:set> <s:iterator value="#customers"> <s:property value="key"/>=<s:property value="value"/> </s:iterator>
Set标签用于将某个值放入指定范围。 scope:指定变量被放置的范围,该属性可以接受application、session、request、page或action。
如果没有设置该属性,则默认放置在OGNL Context中。
value:赋给变量的值.如果没有设置该属性,则将ValueStack栈顶的 值赋给变量。
<s:set name="name" value="'kk'" /> 注意:需要单引号 引起来 <s:property value="#name"/>
取request、session、application中的数据
<% request.setAttribute("name","QQ姐"); request.getSession().setAttribute("username","小马同志"); application.setAttribute("online",22); %> <s:property value="#request.name"/> <s:property value="#session.username"/> <s:property value="#application.online"/>
对于集合类型,OGNL表达式可以使用in和not in两个元素符号。
in表达式:<s:if test="'foo' in {'foo','momo'}"> 在 </s:if> not in表达式: <s:if test="'foo' not in {'foo','momo'}"> 不在 </s:if>
OGNL还允许使用某个规则(正则表达式)获得集合对象的子集,常用的有以下3个相关操作符。
? 获得所有符合逻辑的元素。
^ 获得符合逻辑的第一个元素。 这个没验证
$ 获得符合逻辑的最后一个元素。
action代码
private List<Student> students; public String list() throws Exception{ students=new ArrayList<Student>(); students.add(new Student(1,"kitty")); students.add(new Student(2,"timmy")); students.add(new Student(3,"jack")); } getter/setter方法
jsp页面
<ul> <!-- 找到所有年龄大于1的student的姓名 --- 结果:[timmy, jack] --> <li>投影(过滤):<s:property value="students.{?#this.sid>1}.{sname}"/></li> <!-- 找到所有年龄大于1的student,取第一个 --- 结果:[timmy] --> <li>投影(过滤):<s:property value="students.{^#this.sid>1}.{sname}"/></li> <!-- 找到所有年龄大于1的student,取第最后个 --- 结果:[jack] --> <li>投影(过滤):<s:property value="students.{$#this.sid>1}.{sname}"/></li> <!-- 找到所有年龄大于1的的集合,判断这个集合是否为空 --- 结果:false--> <li>投影(过滤):<s:property value="students.{$#this.sid>1}.{sname}==null"/></li> </ul>
iterator标签用于对集合进行迭代,这里的集合包含List、Set和数组。
<s:set name="list" value="{'zhangsan','lisi','wangwu','阿飞','小狗子'}"/> <s:iterator value="#list" status="st"> <br/> 共迭代了几个元素:<s:property value="#st.count"/><br/> 当前元素的索引:<s:property value="#st.index"/><br/> <font color='<s:if test="#st.first">red</s:if> <s:elseif test="#st.odd">#ababab</s:elseif> <s:else>green</s:else>' > <s:property/> </font> </s:iterator>
.first 第一个元素
.last 最后元素
<a href="<s:url namespace='/author' action='author_list'> <s:param name='aid' value='11'/> </s:url>" >url标签测试 </a>
生成类似如下路径:
author/author_list.action?aid=11
当标签的属性值作为字符串类型处理时, “%”符号的用途是计算OGNL表达式的值。
<s:a action="student_delete?id=%{id}" οnclick="return confirm('确定要删除吗?')">删除</s:a>
action中取数据 (从action中转到上面的a标签所在的页面)
private Integer id=11; String result = ServletActionContext.getRequest().getParameter("id");
jsp页面
<s:date name="workDate" format="yyyy-MM-dd HH:mm:ss"/>
Action中构造数据(List和Map集合)
public String execute() throws Exception { List<Fruit> fruits=new ArrayList<Fruit>(); fruits.add(new Fruit(1,"苹果")); fruits.add(new Fruit(2,"香蕉")); fruits.add(new Fruit(3,"梨")); ActionContext.getContext().getApplication().put("fruits",fruits); Map<Integer, Fruit> maps=new HashMap<Integer, Fruit>(); maps.put(1,new Fruit(1,"苹果")); maps.put(2,new Fruit(2,"香蕉")); maps.put(3,new Fruit(3,"梨")); ActionContext.getContext().getSession().put("maps",maps); return "success"; }
读取数据
方式一:JSP遍历application和session中的集合
ActionContext.getContext().getSession().put(“fruits”,lists);//在action中把集合放在session作用域中
//注意要添加getter和setter方法,否则不能显示数据
<s:iterator value="#application.fruits" id="f"> <s:property value="#f.id"/>--- <s:property value="#f.name"/><br/> </s:iterator> <br/><br/><br/> <s:iterator value="#session.maps" id="fruit"> <s:property value="#fruit.key"/>### <s:property value="#fruit.value.name"/> </s:iterator>
方式二:遍历action中的属性集合
private List<Product> products;//这个集合属性在action中已经赋值 getter和setter方法
<s:iterator value="products" id="p"> <tr> <td><s:property value="#p.id"/></td> <td><s:property value="#p.name"/></td> <td><s:property value="#p.price"/></td> </tr> </s:iterator>
如果集合为list
<s:checkboxlist name="list" list="{'Java','.Net','RoR','PHP'}" value="{'Java','PHP'}"></s:checkboxlist>
生成如下html代码
<input type="checkbox" name="list" value="Java" id="list-1" checked="checked"/> <label for="list-1" class="checkboxLabel">Java</label> ...
如果集合为MAP
<s:checkboxlist name="map" list="#{1:'Java',2:'.Net',3:'RoR',3:'PHP'}" value="{1,2}" listKey="key" listValue="value"></s:checkboxlist><br/>
生成如下html代码:
<input type="checkbox" name="map" value="1" id="map-1" checked="checked"/> <label for="map-1" class="checkboxLabel">Java</label> ...
如果集合里存放的是javabean
<% Author author1=new Author(1,"小林子"); Author author2=new Author(1,"凤姐"); List<Author> authors=new ArrayList<Author>(); authors.add(author1); authors.add(author2); request.setAttribute("authors",authors); %> <s:checkboxlist name="beans" list="#request.authors" listKey="aid" listValue="aname"></s:checkboxlist>
beans是List类型的集合
异常:org.apache.jasper.JasperException: tag ‘checkboxlist’, field ‘…’, name ‘beans’
解决方案:action中的属性List等需要实例化
该标签的使用和checkboxlist复选框相同,只是把<s:checkboxlist 改成<s:radio>
该标签跟复选框也相同,只是把*<s:checkboxlist>改成<s:select>*
用法如下:
JSP页面
<s:form action="/person_add"> <!-- 防止表单重复提交 第一步:生成id(客户端、服务器)--> <s:token></s:token> 这里放一些表单元素 <s:submit/> </s:form>
在struts.xml中配置
<action name="person_*" class="com.hr.action.PersonAction" method="{1}"> <!-- 防止表单重复提交,第二步:配置"防止表单重复提交拦截器" --> <interceptor-ref name="defaultStack"></interceptor-ref> <interceptor-ref name="token"> <!-- 指定拦截哪些方法需要防止表单重复提交 注意:下面这个add是PersonAction中的一个方法名称; 指定多个方法时,使用,号 如add,edit(一般只有添加,因为修改的话,就算是第一次修改也认为是重复提交,会跳转到invalid.token指定的页面) --> <param name="includeMethods">add</param> </interceptor-ref> <!-- 防止表单重复提交,第三步:如果用户重复提交了跳转到的错误页面--> <result name="invalid.token">/example2/add.jsp</result>
以上配置加入了“token”拦截器和“invalid.token”结果,因为“token”拦截器在会话的token与请求的token不一致时,将会直接返回“invalid.token”结果。
在debug状态,控制台出现下面信息,是因为Action中并没有struts.token和struts.token.name属性,我们不用关心这个错误:
严重: ParametersInterceptor - [setParameters]: Unexpected Exception caught setting ‘struts.token’ on 'class xxx: Error setting expression ‘struts.token’ with value ‘[Ljava.lang.String;@39f16f’
严重: ParametersInterceptor - [setParameters]: Unexpected Exception caught setting ‘struts.token.name’