一、简单例子
说明:本例基于struts2 2.1.6。
1.引入jar包,struts2-core-2.1.6.jar、xwork-2.1.2.jar、ognl-2.6.11.jar、freemarker-2.3.13.jar、commons-logging-1.1.jar、commons-io-1.3.2.jar、commons-fileupload-1.2.1.jar。
2.在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>
3.在src下新建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>
<package name="default" namespace="/" extends="struts-default">
<action name="hello">
<result>/hello.jsp</result>
</action>
</package>
</struts>
4.在地址栏中输入".../hello.action"就会进入hello.jsp页面。(".action"可省略)
二、进一步探索
1.新建com.hanjun.action.HelloAction类:
public class HelloAction {
public String execute(){
return "success";
}
}
说明:可实现Action接口或继承已实现Action接口的类ActionSupport。
配置action时指定class属性:
<action name="hello" class="com.hanjun.action.HelloAction">
<result name="success">/hello.jsp</result>
</action>
说明:进入hello.jsp前会执行HelloAction的execute方法,根据execute方法的返回值,在该action的配置中打到name为对应值的result,进入指定页面。如果返回值为success,result的name可不配置。
2.action不指定class属性时,默认执行ActionSupport类。action不指定执行方法时,默认为execute方法。action的method属性可指定进入什么方法。
3.可以通过url来指定进入什么方法:hello!addUser.action、hello!deleteUser.action:
public class HelloAction {
public String addUser(){
return "addUser";
}
public String deleteUser(){
return "deleteUser";
}
}
<action name="hello" class="com.hanjun.action.HelloAction">
<result name="addUser">/adduser_success.jsp</result>
<result name="deleteUser">/deleteuser_success.jsp</result>
</action>
4.通配符(精确配置优先于通配符配置):
<action name="Student*" class="com.hanjun.action.StudentAction" method="{1}">
<result>/Student{1}_success.jsp</result>
</action>
<action name="*_*" class="com.hanjun.action.{1}Action" method="{2}">
<result>/{1}_{2}_success.jsp</result>
</action>
说明:Studentadd.action(符合Student*)进入add方法返回Studentadd_success.jsp页面;
Student_delete.action(符合*_*)进入delete方法,返回Student_delete.jsp页面;(*_*中的_不能用!)
5.多个struts配置文件:在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>
<constant name="struts.devMode" value="true" />
<include file="login.xml" />
</struts>
login.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>
<package name="login" extends="struts-default" namespace="/login">
<action name="login*" class="com.hanjun.action.LoginAction{1}">
<result>/user_login_success.jsp</result>
</action>
</package>
</struts>
6.如果action不存在则进入默认action(不会拦截web.xml中配置的首页或jsp页面):
<default-action-ref name="index"></default-action-ref>
<action name="index">
<result>/default.jsp</result>
</action>
7.result的type属性:
dispatcher : forward到一个页面
redirect : sendRedirect到一个页面
chain : forward到另一个action(值为另一个aciton的name值)
redirectAction : sendRedirect到另一个actoion
说明:如果转到另一个包的action,则配置为:
<result type="redirectAction">
<param name="actionName">r2</param>
<param name="namespace">/包名</param>
</result>
8.共用的result,配置在package内:
<global-results>
<result name="mainpage">/main.jsp</result>
</global-results>
说明:如果另一个包也想用:则把另一个package的extends设为本包的name值。(包继承)
9.可以在result中使用${变量名}获取action中的值。
10.配置异常映射,当所请求的antion方法出现异常时进入指定页面:
<global-results>
<result name="error">/error.jsp</result>
</global-results>
<global-exception-mappings>
<exception-mapping result="error" exception="java.lang.Exception"></exception-mapping>
</global-exception-mappings>
也可以单独使用:
<action name="*_*" class="com.hanjun.action.{1}Action" method="{2}">
<result>/{1}_{2}.jsp</result>
<exception-mapping result="error" exception="java.sql.SQLException" />
<result name="error">/error.jsp</result>
</action>
三、URL参数接收
1.在action中声明变量:
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
说明:可接收url参数name的值:如url?name=zhangsan
2.在action中声明实体类:
private User user;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
说明:可接收url参数user.name、user.age ...的值(User类的属性也要有对应的get、set方法)。
3.action实现ModelDriven接口(不常用):
public class UserAction extends ActionSupport implements ModelDriven<User>{
private User user = new User();
public String add() {
System.out.println("name=" + user.getName());
System.out.println("age=" + user.getAge());
return SUCCESS;
}
@Override
public User getModel() {
return user;
}
}
说明:这样还可以直接接收name、age参数。注意:user属性必须手动初使化。
四、常用属性
struts2常量读取的先后顺序(后读取的覆盖先读取的):
struts-default.xml、struts-plugin.xml、struts.xml、struts.properties、web.xml
1.开发模式,设为true修改struts2配置后不用重启服务器:
<constant name="struts.devMode" value="true" />
2.设置接收参数时的编码:
<constant name="struts.i18n.encoding" value="GBK" />
说明:struts2.1.6在web.xml中配置的org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter 对此配置无效。配置为struts2.0的filter:org.apache.struts2.dispatcher.FilterDispatcher就可以。
3.设置后缀:
<constant name="struts.action.extension" value="action,," />
4.设置可以调用静态方法:
<constant name="struts.ognl.allowStaticMethodAccess" value="true"></constant>
五、数据效验
1.进入方法后手动效验:
public String add() {
if(name == null || !name.equals("admin")) {
this.addFieldError("name", "name is error");
this.addFieldError("name", "name is too long");
return ERROR;
}
return SUCCESS;
}
在错误页面引入struts2的标签:
<%@taglib uri="/struts-tags" prefix="s" %>
打印错误信息的两种方法:
<s:fielderror fieldName="name"/> //以ul方法展示,两个错误信息各为一个li
<s:property value="fieldErrors.name[1]"/> //打印key为name的第一个错误信息"name is error"
六、获取JSP对象
1. 获取jsp对象有四种方法,第一种:
Map request = (Map)ActionContext.getContext().get("request");
Map session = ActionContext.getContext().getSession();
Map application = ActionContext.getContext().getApplication();
2.第二种是第一种的注入形式,实现RequestAware,SessionAware, ApplicationAwar接口:
private Map<String, Object> request;
private Map<String, Object> session;
private Map<String, Object> application;
@Override
public void setRequest(Map<String, Object> request) {
this.request = request;
}
@Override
public void setSession(Map<String, Object> session) {
this.session = session;
}
@Override
public void setApplication(Map<String, Object> application) {
this.application = application;
}
3.第三种:
HttpServletRequest request = ServletActionContext.getRequest();
HttpSession session = request.getSession();
ServletContext application = session.getServletContext();
4.第四种是第三种的注入形式,实现ServletRequestAware接口:
private HttpServletRequest request;
private HttpSession session;
private ServletContext application;
@Override
public void setServletRequest(HttpServletRequest request) {
this.request = request;
this.session = request.getSession();
this.application = session.getServletContext();
}
5.说明:
第1种和第2种获取的request不能获取从页面传过来的参数值。
七、OGNL
在jsp中引入<%@ taglib uri="/struts-tags" prefix="s" %>
1. 获取并打印username属性:
<s:property value="username"/> //完整写法为:<s:property value="%{username}"/>
2.获取并打印user的age属性:
<s:property value="user.age"/>
3.获取并打印cat的friend的name属性:
<s:property value="cat.friend.name"/>
4.直接使用java的方法:
<s:property value="password.length()"/>
5.防问静态方法:
<s:property value="@com.hanjun.struts2.ognl.S@s()"/>
说明:需要在struts.xml中配置:
<constant name="struts.ognl.allowStaticMethodAccess" value="true"></constant>
6.防问静态变量:
<s:property value="@com.hanjun.struts2.ognl.S@STR"/>
7.访问Math类的静态方法:
<s:property value="@@max(2,3)" />
8.访问普通类的构造方法:
<s:property value="new com.hanjun.struts2.ognl.User(8)"/>
9.防问集合:
<li>访问List:<s:property value="users"/></li>
<li>访问List中某个元素:<s:property value="users[1]"/></li>
<li>访问List中元素某个属性的集合:<s:property value="users.{age}"/></li>
<li>访问List中元素某个属性的集合中的特定值:<s:property value="users.{age}[0]"/> | <s:property value="users[0].age"/></li>
<li>访问Set:<s:property value="dogs"/></li>
<li>访问Map:<s:property value="dogMap"/></li>
<li>访问Map中某个元素:<s:property value="dogMap.dog101"/> | <s:property value="dogMap['dog101']"/> | <s:property value="dogMap[\"dog101\"]"/></li>
<li>访问Map中所有的key:<s:property value="dogMap.keys"/></li>
<li>访问Map中所有的value:<s:property value="dogMap.values"/></li>
<li>访问容器的大小:<s:property value="dogMap.size()"/> | <s:property value="users.size"/> </li>
10.投影:
<li>获取age=1的第一个user:<s:property value="users.{?#this.age==1}[0]"/></li>
<li>获取age大于1的第一个user:<s:property value="users.{^#this.age>1}"/></li>
<li>投影age大于1的最后一个user:<s:property value="users.{$#this.age>1}"/></li>
获取当前堆栈中的第一个元素:<s:property value="[0]"/> //[0]也可以用top
11.lambda表达式:
#fact=:[#this<=1?1:#this*fact(#this-1)],#fact(30H) //声明一个使用递归来计算阶乘的函数然后调用它
八、常用标签
在jsp中引入<%@ taglib uri="/struts-tags" prefix="s" %>
1.property:
<s:property value="username"/> //获取属性username的值
<s:property value="admin" default="管理员"/> //default设置默认值
<s:property value="'<hr/>'" escape="false"/> //解析并输出html元素。
<s:property value="#request.adminName" /> //从request中取值
<s:property value="#adminName" /> //从ActionContext中取值
<s:property value="#parameters.age[0]" /> //获取url参数的值
2.set:
<s:set var="adminPassword" value="password" scope="session"/>
说明:scope的值有:page、request、session、action、application
3.bean:
<s:bean name="com.hanjun.struts2.tags.Dog" var="myDog">
<s:param name="name" value="'oudy'"></s:param>
</s:bean>
拿出值:
<s:property value="#myDog.name"/>
4.if:
<s:if test="#age < 0">wrong age!</s:if>
<s:elseif test="#parameters.age[0] < 20">too young!</s:elseif>
<s:else>yeah!</s:else>
5.iterator:
<s:iterator value="{'aaa', 'bbb', 'ccc'}" var="x">
<s:property value="#x.toUpperCase()"/> |
</s:iterator>
<s:iterator value="{'aaa', 'bbb', 'ccc'}" status="status">
<s:property/> |
遍历过的元素总数:<s:property value="#status.count"/> |
遍历过的元素索引:<s:property value="#status.index"/> |
当前是偶数(从1开始)?:<s:property value="#status.even"/> |
当前是奇数(从1开始)?:<s:property value="#status.odd"/> |
是第一个元素吗?:<s:property value="#status.first"/> |
是最后一个元素吗?:<s:property value="#status.last"/>
<br />
</s:iterator>
6.date:
<s:date name="date" format="yyyy-MM-dd HH:mm:ss"/> //name指定ognl
九、转换器
1.例如在MyAction中有Point point类,Point类有int x,int y属性。页面表单有一文本框 name="point",用户输入"3,4"。那么就需要写一个转换器将3和4分别装入point的x和y。
2.新建PointConverter类,继承org.apache.struts2.util.StrutsTypeConverter类:
public class MyPointConverter extends StrutsTypeConverter {
public Object convertFromString(Map context, String[] values, Class toClass) {
Point p = new Point();
String[] strs = (String[]) values;
String[] xy = strs[0].split(",");
p.x = Integer.parseInt(xy[0]);
p.y = Integer.parseInt(xy[1]);
return p;
}
public String convertToString(Map context, Object o) {
Point p =(Point) o;
return p.getX()+","+p.getY();
}
}
3.在MyAction同包下新建文件MyAction-conversion.properties:
point=com.hanjun.converter.MyPointConverter
十、拦截器
1.新建类实现com.opensymphony.xwork2.interceptor.Interceptor:
public class MyInterceptor implements Interceptor {
public void destroy() {
// TODO Auto-generated method stub
}
public void init() {
// TODO Auto-generated method stub
}
public String intercept(ActionInvocation invocation) throws Exception {
long start = System.currentTimeMillis(); //action方法执行前
String result = invocation.invoke();
long end = System.currentTimeMillis(); //action方法执行后
System.out.println("action time = " + (end - start));
return result;
}
}
2.struts.xml中:
<interceptors>
<interceptor name="myInterceptor" class="com.hanjun.interceptor.MyInterceptor"></interceptor>
</interceptors>
<action name="test" class="com.hanjun.action.TestAction">
<result>/test.jsp</result>
<interceptor-ref name="myInterceptor"></interceptor-ref>
<interceptor-ref name="defaultStack"></interceptor-ref>
</action>
或:
<interceptors>
<interceptor name="myInterceptor" class="com.hanjun.interceptor.MyInterceptor"></interceptor>
<interceptor-stack name="myStack">
<interceptor-ref name="myInterceptor"></interceptor-ref>
<interceptor-ref name="defaultStack"></interceptor-ref>
</interceptor-stack>
</interceptors>
<action name="test" class="com.hanjun.action.TestAction">
<result>/test.jsp</result>
<interceptor-ref name="myStack"></interceptor-ref>
</action>
注:当package的extends="struts-default"时,action默认的拦截器为defaultStack。但在action内指定自定义拦截器或拦截器堆栈后,需手动添加defaultStack。
3.指定拦截的方法:
MyInterceptor继承MethodFilterInterceptor类,此类有两个set属性:excludeMethods(要排除的方法名集合);includeMethods(要拦截的方法集合);
<interceptor name="myInterceptor" class="com.bjsxt.interceptor.MyInterceptor">
<param name="includeMethods">doLogin,doRegister</param>
</interceptor>
或在action设置拦截器时指定拦截方法:
<action name="test" class="com.bjsxt.action.TestAction">
<result>/test.jsp</result>
<interceptor-ref name="myStack">
<param name="myInterceptor.includeMethods">doLogin,doRegister</param>
</interceptor-ref>
</action>
十一、禁止重复提交
1.在页面的form中添加:
<s:token></s:token>
注:每次生成一个不同的标识存入session,提交后会校验。
2.struts.xml:
<action name="user" class="com.hanjun.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>
注:token为struts2定义好的拦截器,如果是重复提交则返回"invalid.token",所以定义一个result,重复提交时进入指定页面。
十二、国际化
1.在src下新建msg_en_US.properties和msg_zh_CN.properties。(msg自定义)
login.username=用户名://msg_en_US.properties中
login.username=username: //msg_zh_CN.properties中
2.struts.xml中添加:
<constant name="struts.custom.i18n.resources" value="msg"></constant>
3.jsp页面(显示结果根据网页设置的语言而不同):
<s:text name="login.username"/> 或 <s:property value="getText('login.username')"/>
注:也可以使用url参数request_locale=en_US或request_locale=zh_CN手动指定,不过此url必须为struts的url。struts拦截到此参数后会存入session,session失效前不用重新设置。
4.使用占位符:
welcome.msg=欢迎你:{0} //properties中
<s:text name="welcome.msg">
<s:param value="username"></s:param> //value指定ognl表达式
</s:text>
十三、文件上传
1.页面:
<form action="uploadAction.action" method="post" enctype="multipart/form-data">
<input type="file" name="file" value="请选择"><br/>
<input type="submit" value="提交">
</form>
2.UploadAction:
private File file;
private String fileFileName;
private String fileContentType;
//上面三个属性get、set方法...
public String execute() throws Exception {
String path = ServletActionContext.getServletContext().getRealPath(
"/upload");
File dir = new File(path);
if (!dir.exists()) {
dir.mkdir();
}
FileInputStream fis = new FileInputStream(file);
BufferedInputStream bis = new BufferedInputStream(fis);
FileOutputStream fos = new FileOutputStream(new File(dir, fileFileName));
BufferedOutputStream bos = new BufferedOutputStream(fos);
byte[] buf = new byte[1024];
for (int len = bis.read(buf); len != -1; len = bis.read(buf)) {
bos.write(buf, 0, len);
}
bos.flush();
bos.close();
bis.close();
return super.execute();
}
3.struts2默认上传文件的最大限制为2M,可以修改全局的上传大小限制:
<constant name="struts.multipart.maxSize" value="999000000"></constant> //999M
注:当上传的文件超过限制大小时,后台报"the request was rejected because its size (文件大小) exceeds the configured maximum (2097152)"并返回input。
可以重写ActionSupport类的addActionError方法将错误信息存入ActionError:
public void addActionError(String anErrorMessage) {
// 这里要先判断一下,是我们要替换的错误,才处理
if (anErrorMessage.startsWith("the request was rejected because its size")) {
Matcher m = Pattern.compile("\\d+").matcher(anErrorMessage);
String s1 = "";
if (m.find()){
s1 = m.group();
}
String s2 = "";
if (m.find()){
s2 = m.group();
}
// 偷梁换柱,将信息替换掉
super.addActionError("你上传的文件大小(" + s1 + ")超过允许的大小(" + s2 + ")");
} else {// 否则按原来的方法处理
super.addActionError(anErrorMessage);
}
}
4.单独设置一个action的上传文件最大值:
<action name="uploadAction" class="com.hanjun.action.UploadAction">
<result>/upload_success.jsp</result>
<result name="input">/test.jsp</result>
<interceptor-ref name="defaultStack">
<param name="fileUpload.maximumSize">5000000</param>
</interceptor-ref>
</action>
注:这里设置的大小应小于全局大小。如果文件大于这里设置的大小而小于全局设置的大小,直接返回input,没有错误信息(我的试验结果)。
十四、文件下载
1.jsp:
<form action="download.action" method="post">
<input name="filename" value="tisr.log">
<input type="submit" value="下载">
</form>
2.DownloadAction:
public class DownloadAction extends ActionSupport {
private String filename;
private String directory;
public String execute() throws Exception {
return SUCCESS;
}
public InputStream getInputStream() throws Exception {
String dir = directory + filename;
return new FileInputStream(dir); // 如果dir是绝对路径
//如果dir是Resource下的相对路径
//return ServletActionContext.getServletContext().getResourceAsStream(dir);
}
public String getFilename() {
return filename;
}
public void setFilename(String filename) {
this.filename = filename;
}
public void setDirectory(String directory) {
this.directory = directory;
}
}
3.struts.xml:
<action name="download" class="com.bjsxt.action.DownloadAction" >
<param name="directory">D:/</param>
<result name="success" type="stream">
<!--- 指定下载文件的内容类型,text/plain是默认类型 application/octet-stream为无限制-->
<param name="contentType">application/octet-stream</param>
<!--- inputName默认值是inputStream,代表从getInputStream方法获取流 -->
<param name="inputName">inputStream</param>
<!---动态获取文件名,从Action中的取得filename-->
<param name="contentDisposition">
attachment;filename="${filename}"
</param>
<param name="bufferSize">2048</param>
</result>
<result name="input">/downloadError.jsp</result>
</action>