struts2的学习笔记
struts2的文档,主要看主页面的Guides下的内容
1、在struts.xml中加<constant name="struts.devMode" value="true"/>设置为开发模式,会自己检测配置文件的改动,在产品发布时设置value="fasle"
<constant name="struts.i18n.encoding" value="gbk"/>设置struts2的国际化(internationalization因为在i和n之间有18个字母)
constant的name的属性可以在struts2-core-2.16.jar下的org.apache.struts2下的static下的default.properties文件中找
2、在配置文件package标签的配置
namespace决定了action的访问路径,默认为""(不写namespace),可以接受所有路径的action。
例如:
<package name="main" extends="struts-default">
<action name="index">
<result>/namespace.jsp</result>
</action>
</package>
对于这样的配置,url可以是:http://127.0.0.1:8080/appname/index.action或是http://127.0.0.1:8080/appname/xxx/index.action都可以响应。
namespace可以写为/,或者/xx,或者/xx/yy,对应的action说路径为/index.action,/xx/index.action,/xx/yy/index.actiion
一般namespace用的是模块名进行命名。
3、struts2中的三种action
1) 普通的java类,但要有一个方法:public String execute()
2) 直接实现Action接口
3) 继承抽象类ActionSupport(一般都只用这种,因为ActionSupport封装了许多有用的方法)
4、路径问题
struts2中的路径问题是根据action的路径,而不是jsp路径来确定的,所以不要使用相对路径。解决方法:
1) 用redirect方式解决;
2) 在jsp中用request.getContextPath方式来拿到webapp的路径(用webapp的相对路径);
例如<a href="${request.getContextPath}/index.jsp">首页</a>
3) 使用myeclipse经常用的,指定basePath
<% String path=request.getContextPath;
String basePath=request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";%>
在页面的head标签中加入<base href="basePath"/>,用的时候直接写<a href="index.jsp">首页</a>
或者 <a href="<%=basePath%>index.jsp">首页</a>
5、struts中对action的三种配置(action继承自ActionSupport)
例如:
com.aliang.action.UserAction
添加用户的方法 add_User()、删除用户的方法delete_User()
1) 一个action的方法对应一个action的配置
<package name="user" extends="struts-default" namespace="/user">
<action name="add_User" class="com.aliang.action.UserAction" method="add_User">
<result>/add_User_Success.jsp</result>
</action>
<action name="delete_User" class="com.aliang.action.UserAction" method="delete_User">
<result>/delete_User_Success.jsp</result>
</action>
</package>
访问方法 http://127.0.0.1:3306/webappname/user/add_User 访问的是UserAction的add_User()方法
2) 同一个action类的所有action方法配置在一action标签中(DMI 动态方法调用)
<package name="user" extends="struts-default" namespace="/user">
<action name="user" class="com.aliang.action.UserAction">
<result>/result.jsp</result>
</action>
</package>
访问方法 http://127.0.0.1:3306/webappname/user/user!add_User或者 http://127.0.0.1:3306/webappname/user/user!delete_User
但这种的result中要么共享同一个、要么每个返回一种,都在action标签中配置一种Result
3) 使用*来通配
<package name="user" extends="struts-default" namespace="/user">
<action name="*-*" class="com.aliang.action.{2}Action" method="{1}_{2}">
<result>/{1}_{2}_Success.jsp</result>
</action>
</package>
访问方法 http://127.0.0.1:3306/webappname/user/add_User访问调用的是com.aliang.action.UserAction的add_User方法,跳转的页面是add_User_Success.jsp
这样的配置可以用于多个action,但要求命名要统一。
6、action封装参数(action要继承处ActionSupport)
1) 用action的属性来接受并封装参数
为属性添加set和get方法,并在页面中的名字要与set方法的后部分相同
2) 用对象封装属性(域模型domain model)
在action的属性为对象,并为其添加set和get方法
如有一个 User user=null;的属性
在页面中用user.name、user.age将参数的值传给action的user属性
3) 用ModelDriven接受参数,(让action实现ModelDriven接口)
例如:有如下的action
public class UserAction extends ActionSupport implements ModelDriver<User>{
private User user=new User();
@Override
public User getModel(){
return user;
}
public String add(){
System.out.println("user.name="+name);
System.out.println("user.age="+age);
return SUCCESS;
}
}
struts2先new 一个UserAction,当struts2发现该action实现了ModelDriven接口,就会调用它的getMode()方法,并返回一个Mode(User),再调用mode的get方法将其set到user的属性名相对应的属性
7、struts2的中文问题
一般如果要使用中文,form的method一般设置为post
2.1.7版本以后加<constant name="struts.i18n.encoding" value="gbk"/> 国际化后可以正常显示中文,不加默认是utf8
2.1.6版本及以前的版本在web.xml中的使用
<filter-class>org.apache.struts2.dispatch.FilterDispatcher</filter-class>这个过滤器,可以将中文乱码问题解决
但apache推荐使用的是新版本的过滤器,新版本的在2.1.6之后的版本中才支持国际化解决中文问题,或者也可以使用spring来解决这个问题新过滤器是:
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
7、数据验证、取得web元素
ActionSupport的addFieldError("name","name is error");
在action中调用this.addFieldError("name","name is error");当name属性验证没通过时设置错误信息"name is error",可以对同一个属性加多次错误信息。在页面中用struts2的标签来显示错误,要使用struts2标签首先要引入标签库:
<%@taglib uri="/struts-tags" prefix="s"%>
struts-tags.tld在struts2-core-2.16.jar下的META-INF目录下
然后使用标签<s:fielerror fieldName="name" theme="simple"/>fileName属性对应action中的addFieldError("name","name is error")方法的"name"。这样的样式固定,要加样式,在css中要定义一个与生成的标签的class属性名相同的样式来控制
<s:debug></s:debug>该标签会生成一个用于调试程序的链接,该链接可以查看Value Stack Contents、Stack Context(访问stack Context中的值要加#,例如<s:property value="#request.student.name">)中的详细信息
<s:property value="属性名"/>只能用于取Value Stack Contents和Stack Context中的值
value="属性名"取Value Stack Contents中的值、value="#属性名"取Stack Context中的值。
这里取出的是一个map,例如<s:property value="errors"/>取出的是:{name={name is error}},而map的value又是一个数组,如<s:property value="errors.name"/>取出的是:[name is error];正确的用法是:<s:property value="errors.name[0]"/>这样取出的值才是:name is error。errors.name[0]这样的表达式就是一个OGNL表达式
8、取得Map类型的request、session、application和真实类型的HttpServletRequest、HttpSession、ServletContext
1) 取得Map类型的request、session、application:
方法1(一般加在构造方法中)
request=(Map)ActionContext.getContext().get("request");
session=ActionContext.getContext().getSession();
application=ActionContext().getContext().getApplication();
方法2(一般都只用这种方法,session最常用)
让action实现RequestAware、SessionAware、ApplicationAware接口
在action中加map类型的request、session、application属性(带get和set方法)就可以直接使用,因为只要你的程序实现了xxxAware接口,struts2的IOC机制会自动将xxx属性注入进来。
2) 取得真实类型的HttpServletRequest、HttpSession、ServletContext
方法1(一般加在构造方法中)
request=ServletActionContext.getRequest();
session=request.getSession();
application=session.getServletContext();
方法2(使用IOC)
让action实现ServletRequestAware接口
为action定义:
HttpServletRequest request;
HttpSession session;
ServletContext application;
这个三个属性(有set和get方法),struts容器会自动将这三个属性注入进来
直接使用就可以。
9、模块包含(包含struts的子配置文件)
在struts.xml的<struts></struts>之内加<include file="xxx.xml">将xxx.xml文件包含进来
10、默认action
在struts.xml的<struts></struts>之内加<default-action-ref name="index"></default-action-ref>当用户请求的action找不到时默认会找index.action
11、Result的配置
1) type 不配置默认是:dispatcher(相当于forward到一个jsp页面)
type的取值:dispatcher(forward到一个jsp或者html页面)、redirect(重定向到另一个页面)、chain(跳转到另外一个action)、redirectAction(重定向到另一个action)、freemarker、httpheader(http头信息)、stream(一般下载时使用)、velocity(和freemarker差不多)、xslt、plaintext(显示页面的源码、html的源码,一般做教学时会用到)、tiles
2) 全局的result
<global-results>
<result name="mainpage">main.jsp</result>
</global-results>
可以加在<package></package>标签内,如果一个package中的action想访问另一个包中的global-results可以将要访问的action所在的package的extends属性设置为想要访问的global-results所在的package的名字
3) 动态的result
<action name="user" class="com.aliang.action.UserAction">
<result>${r}</result>
</action>
r是UserAction中的一个String类型的成员变量(不是属性)用来在action中动态的保存要跳转的jsp页面的路径。$用来在配置文件中取Value Static取值
4) 利用result传递参数
<action name="user" class="com.aliang.action.UserAction">
<result type="redirect">/user_success.jsp?t=${type}</result>
</action>
type是action中的属性
一个request一个Value Stack Contents(值栈)所以当原始页面请求user.action的时候产生一个值栈,而当原始页面再次请求user_success.jsp时没有action所以产生的值栈是空的,因此在user_success.jsp页面不能用<property value="t"/>来取值,这样取是在Value Stack Contents中取值因此取到的也是空
12、OGNL(Object Graph Navigation language)
当使用user.xxx给user传参数时才创建user实例,或者在action直接先new一个对象,否则在值栈中是一个空值。因此,如果想要初始化domain model时可以给要初始的domain model传一个参数,或直接在action中new 一个domain model
1) 访问值栈中的action的普通属性 <s:property value="username"/>
2) 访问值栈中对象的普通属性(get set方法) <s:property value="user.age"/>
3) 访问值栈中对象的对象类型的属性(get set方法) <s:property value="cat.friend.name"/>
4) 访问值栈中的普通方法 <s:proerty value="password.length()"/>
5) 访问action的普通方法 <s:property value="actionDoIt()"/>
--------------------------------------------------------
在Struts2.0中访问静态方法可直接按下面的写,在struts2.1及以后还要在struts.xml中的<struts></struts>标签中加<constant name="struts.ognl.allowStaticMethodAccess" value="true"></constant>
6) 访问一个类的静态方法 <s:property value="@com.aliang.Test@getStr()"/>,访问com.aliang.Test类的静态方法getStr()
7) 访问一个类的静态属性 <s:property value="@com.aliang.Test@NAME"/>
8) 访问Math类的静态方法 <s:property value="@@max(2,3)"/>
---------------------------------------------------------
9) 访问普通类的构造方法<s:property value="new com.aliang.User(8)"/>返回一个利用相应构造方法构造的User类型的对象
10) 访问List <s:property value="users"/>users是一个List<User>;
11) 访问List中某个元素 <s:property value="users[1]"/>
12) 访问List中元素某个属性的集合 <s:property value="users.{age}"/>
13) 访问List中元素某个属性的集合中特定值 <s:property value="users.{age}[0]"/>或者<s:property value="users[0].age"/>
14) 访问Set <s:property value="dogs"/> dogs是Set<Dog>
15) 访问Set中某个元素 <s:property value="dogs[1]"/>这样是访问不到的,因为set没有顺序
16) 访问Map <s:property value="dogMap"/> dogMap是Map<String,Dog>
17) 访问Map中某个元素 <s:property value="dogMap.dog001"/>或者<s:property value="dogMap['dog001']"/>或者<s:property value="dogMap[/"dog001/"]"/> dog001是key的值
18) 访问Map中所有的key <s:property value="dogMap.keys"/>
19) 访问Map中所有的values <s:property value="dogMap.values"/>
20) 访问集合的大小 <s:property value="dogMap.size()"/>或者<s:property value="dogMap.size"/>
21) project投影(过滤) ?代表过滤条件,^代表开头,$代表结束
(1) <s:property value="users.{?#this.age==1}.{age}"/>找出users中age等1的所有User的age的集合 其中users是一个List<User>,this是指循环遍历的当前对象。<s:property value="users.{?#this.age==1}"/>得到的也是一个集合
(2) <s:property value="users.{^#this.age>1}.{age}"/>找出users中age大于1的集合中的第一个user的age
(3) <s:property value="users.{$#this.age>1}.age"/>找出users中age大于1的集合中的最后一个user的age
(4) <s:property value="users.{$#this.age>1}.{age}==null"/>判断集合是不是空
22) []访问集合
<s:property value="[0]"/>这样会从Value Stack的栈顶开如访问整个栈
13 struts标签
1) 通用标签
struts中标签的属性是Object类型的都可以解析成一个ognl表达式
a) property(value default escape)
default给属性设置默认值<s:property value="admin" default="管理员"/>
escape用于设置是否显示value中的html代码,默认是true <s:property value="<hr/>" escape="false"/>就会显示一条水平线
b) set(id name scope value var)用于给变量赋值、重新命名
id 和 name都不赞成使用
scope定义默认的范围是action 在request和Action Context中都有变量
如果指定了其它的scope则在action中不一定有这个对象
<s:set var="adminName" value="username"/> username是action的一个User类型的属性
c) bean(name var) 定义bean,并使用param来设定新的属性值
<s:bean name="com.aliang.struts.Dog" var="myDog">
<s:param name="name" value="'lostDog'"></s:param>
</s:bean>
这里定义了一个Dog类型的实例,并给其name属性赋值为"lostDog"因为value的值是一个对象类型,如果不加'号会当成ognl表达式处理
d) include存在中文字符编码的问题
<s:set var="incPage" value="'/_include1.html'">
<s:include value="%{#incPage}"></s:include>
include的value是string类型,在这儿想用ognl表达式 %{XXX}强制把XXX当作ognl表达式使用
e) param(name value)
f) debug用于调试
<s:debug>调试</s:debug>
2) 控制标签
if elseif else
<s:if test="#parameters[0].age < 0">wrong age!</s:if>
<s:elseif test="parameters[0].age < 22">too young!</s:elseif>
<s:else>you can marray!</s:else>
iterator
可以遍历:collections map 实现了enumeration或iterator接口的类 array
<s:iterator value="{23,22,33}">
<s:property />
</s:iterator>
<s:iterator value="{'abc','aa','ab'}" var="x">
<s:property value="#x.toUpperCase()"/>
</s:iterator>
<s:iterator value="{'abc','aa','ab'}" status="sta">
遍历过的元素的总数:<s:property value="#sta.count"/>
遍历过的元素的索引:<s:property value="#sta.index"/>
当前遍历的个数是否是偶数:<s:property value="#sta.even"/>
当前遍历的个数是否是奇数:<s:property value="#sta.odd"/>
当前遍历的个数是否是第一个元素:<s:property value="#sta.first"/>
当前遍历的个数是否是最后一个元素:<s:property value="#sta.last"/>
</s:iterator>
遍历map
<s:iterator value="{1:'abc',2:'aa',3:'ab'}">
<s:property value="key"/>或者<s:property value="value"/>
</s:iterator>
<s:iterator value="{1:'abc',2:'aa',3:'ab'}" var="x">
<s:property value="#x.key"/>或者<s:property value="#x.value"/>
</s:iterator>
subset从集截取一部分
<s:subset source="myList" count="12" start="3" var="mySubset">
从myList这个集合中的第3个元素开始截12个元素出来,生成一个名叫mySubset的子集合
</s:subset>
3) UI标签
theme主题有4种值(simple xhtml(默认) css ajax)
4) AJAX标签
5) $、#、%的区别
$用于i18n和struts配置文件
#取得Action Context的值
%将原本的文本属性解析为ognl,对于本来就是ognl的属性不起作用
14、异常处理
有异常一般都抛到表示层来处理
在struts.xml中配置如下:
<action name="category" class="com.aliang.bbs.action.CategoryAction">
<result>/WEB-INF/a.jsp</result>
<exception-mapping result="error" exception="java.sql.SQLException"/>
<result name="error">/WEB-INF/error.jsp</result>
</action>
全局异常的配置(大多数都是这种异常处理):
在<package></package>标签中:
<global-results>
<result name="error">/WEB-INF/error.jsp</result>
</global-results>
<global-exception-mappings>
<exception-mapping result="error" exception="java.sql.SQLException"/>
</global-exception-mappings>
注意:<global-result>要配在前边
15、国际化
java.util.Locale
java.util.ResourceBundle
ResourceBundle rb=ResourceBundle.getBundle("app",Locale.US);
第一个参数"app"传入的参数是资源文件的前部分,第二个参数代表的是国家Locale.CHINA
rb.getString("资源文件中的key");
这样会自动根据Locale的值来加载相应的资源文件
资源文件是:app_en_US.properties app_zh_CN.properties
注意:在资源文件中不能放中文,要使用native2ascii 将其转换为utf8格式的,或者用myeclipse的PropertiesEditor(一个插件,要自己安装)来打开就直接写中文
struts2的资源文件分三个级别:Action级-Package级-Application级
Action级(很少用):要跟action在同个目录且名字要相同,如LoginAction对应的资源文件:LoginAction_en_US.properties,在页面用<s:property value="getText('login.username')"/>参数login.username是资源文件中的key,要换语言只要将浏览器中的语言改变就会自动选择对应的资源文件,getText()方法是ActionSupport中的方法
packate级(很少使用):资源文件的名字必须要用package_en_US.properties
Application级(一般只用这种):资源文件的命名根据自己起,如XXX_en_US.properties,放在src目录下。在struts.xml加入<constant name="struts.custom.i18n.resources" value="XXX"/>这里的value的值是资源文件名的开头部分。
三种级别的国际化在页面中的使用都是 <s:property value="getText('资源文件中的key')"/>或<s:text value="资源文件中的key"/>来取得相应的语言表达的内容
在资源文件中使用点位符:
例如在资源文件中有: welcome.message=欢迎您:{0}登陆本系统
的内容,在页面中可以用:
<s:text name="welcome.message">
<s:param value="username"></s:param>
</s:text>
来取得资源文件并为占位符0处填充值
动态语言切换:在请求的url后边加:?request_locale=en_US或者zh_CN
16、重复提交的问题
在struts2中有token拦截器,专用来处理重复提交
<action name="user" class="com.aliang.action.UserAction">
<result>addOK.jsp</result>
<interceptor-ref name="defaultStack"></interceptor-ref>
<interceptor-ref name="token"></interceptor-ref>
<result name="invalid.token">error.jsp</result>如果重复提交,跳转的页面
</action>