Struts2的知识详解 http://www.cnblogs.com/djoker/p/6219691.html
Struts2源码详解 http://www.cnphp6.com/archives/16561
Struts2学习总结 https://my.oschina.net/lunqi/blog/504695
Struts2
学习版本: 2.3.24.1
框架认识
框架学起来比较简单
但也有难的地方,难是难在底层原理
用了框架之后,写代码就非常简单,因为框架会完成一部分代码,
我们只要按框架的规则去使用就可以了
学习框架的方法:
与学习初级基础有所不同,
基础学习都是学习基本语法,完成简单的案例,逐步理解面向对象的编程思想.
框架属于中高级的学习,要运用面向对象的进行编程,接口编程
能够自已完成业务.熟悉业务.能够架构一个项目,属性框架的原理
框架学习程度:
相用框架
理解框架原理、走源码
自己编写框架部分
框架的由来
Md1
Md2
Jsp/servlet 的 mvc回顾
当一个方法中有部分代码在不断重复时,抽取出来做一个方法
当很多类在操作同一段代码时,抽出来做一个类
当很多类在做同一类事情的时候,抽出来做一个jar包或做成框架
这就是框架
框架,替程序员完成一部分代码,从而提高开发效率
框架就是一个模板,
Web应用的框架:webwork,jsf, struts1, struts2,springmvc
Struts1 和 webwork 整合 成了struts2
是apatch 下的一个项目,开源的免费的
下载地址:http://struts.apache.org/download.cgi#struts255
目录介绍:
Apps 存放项目案例
Docs存放学习文档
Lib jar包
Src struts2源码
Mvc框架的工作
Servlet:
将页面请求映射到java类,也就是后台
接收并处理页面提交的数据
调用业务逻辑方法处理业务结果,将数据封装准备渲染到页面
控制页面跳转
Mvc的V层:
将页面请求映射到java类,也就是后台
获取并封装好页面提交的数据
渲染数据到页面
控制页面跳转
一个struts2的例子
学习版本: 2.3.24.1
Struts2.3框架搭建步骤
1. 创建一个动态工程
2. 添加LIB(2.3.24.1)
asm-3.3.jar
asm-commons-3.3.jar
asm-tree-3.3.jar
commons-fileupload-1.3.1.jar
commons-io-2.2.jar
commons-lang3-3.2.jar
freemarker-2.3.22.jar
javassist-3.11.0.GA.jar
jsp-api.jar
log4j-api-2.2.jar
log4j-core-2.2.jar
ognl-3.0.6.jar
servlet-api.jar
struts2-core-2.3.24.1.jar
xwork-core-2.3.24.1.jar
3. 配置web.xml
Struts的核心控制器就是过滤器
<?xml version="1.0" encoding="UTF-8"?>
<web-app id="starter" version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
4. 配置struts.xml
注:该名称一定要叫struts.xml,放在src下面
<?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>
注:到这里为止,一个struts2的项目已搭建成功
5. 编写Action
public class HelloStruts {
public String execute(){
System.out.println("你好 execute,struts2");
return "aa";
}
}
注:方法名一定要叫execute
Servlet中默认执行doget/dopost/service方法
方法是受保护的
方法无返回值
方法有参数(HttpServletRequest,HttpServletRespone)
在struts2中,默认执行execute方法
方法是public
方法有返回值String,
方法无参数
6. 配置action
在struts.xml中配置
Struts2是基于包来管理的,那么配置会有体现
<package name="he" extends="struts-default" namespace="/he">
<action name="hello" class="com.ts.action.HelloStruts"><!-- method="test" -->
<result name="aa">/index.jsp</result>
</action>
</package>
package 的name 指包的名称,这个名称可以随便取,保证唯一,用于区分别的包的配置 extends 继承struts-default.xml, 此xml在struts的core包中 namespace 可选配置.名字空间作用相当于java的包,就于区分命名空间,这个名称在url中需要用到
|
action name 指类的名称,在url中要用到,相当于servlet的url,这里不要/ class 类路径 method可选配置. 表示访问到类中的方法名, result 结果集,name就是访问的方法的返回的字符串,与方法的返回字符串相同表示跳转的页面路径,需要带上/
|
一个struts.xml文件中可以有多个package package 有配置原则:一个模块一个package |
package 下的action可以有多个 action配置原则:一个请求一个action 一个请求其实是请求到一个方法中,也就是一个方法配置一个action 方法的指定:action标签中的method指定要执行的方法名 |
action下result 可以有多个,这个表示是action中方法的执行的结果集 action下result配置原则:一个返回结果对应一个result result的name是对应的action的方法返回的字符串 |
Stuts2实现登录
1. Java类
package com.ts.action;
import com.opensymphony.xwork2.ActionSupport;
public class LoginAction extends ActionSupport {
/** * */ private static final long serialVersionUID = 244495613102527095L;
private String name; private String pd;
/** * 登录 * @return */ public String login() { if (name.equals("admin") && pd.equals("admin")) { return "success"; }else { return "fail"; } } /** * 登出 * @return */ public String logout() { return "success"; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getPd() { return pd; }
public void setPd(String pd) { this.pd = pd; } }
|
action中的属性名要和表单中名称一致,struts2会根据名字自动去填充值,
而servlet中需要手动用request.getParamter()
2. 页面 注意action的url
<form action="login/login.action" method="post">
登录名:<input name="name" type="text" /><br>
密码:<input name="pd" type="password" /><br>
<input type="submit" value="登录" />
</form>
3. 欢迎页面
登录成功 <a href="login/logout.action">退出登录</a> |
4. Struts2配置:
<package name="login" extends="default" namespace="/login"> <action name="login" class="com.ts.action.LoginAction" method="login"> <result name="success">/wel.jsp</result> <result name="fail">/500.jsp</result> </action> <action name="logout" class="com.ts.action.LoginAction" method="logout"> <result name="success">/login.jsp</result> </action> </package> |
5. Web.xml
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>*.action</url-pattern>
<!--表示.action结尾的请求需要被struts2核心过滤进行过滤,这里要么是.action要么是/*表示任何请求都进action-->
</filter-mapping>
Sruts2执行流程(重点)
简单的执行流程:
当用户提交一个请求,服务器接收并交给struts2的核心过滤器,
Struts2的过滤器调用一系列拦截器处理(解析struts.xml,验证用户提交的请求)
生成action代理,调用action的execute方法,
执行完后再返回到拦截器,接着返回到过滤器,再将响应返回给服务器,服务器响应给用户
详细流程
走源码
http://blog.csdn.net/snow_7/article/details/51513381 (工作原理和实现流程)
一个请求在Strusts2框架中的处理大概分为以下几个步骤:
1、客户端初始化一个指向Servlet容器(例如Tomact)的请求
2、这个请求经过一系列的过滤器(Filter)(这些过滤器中有一个叫做 ActionContextClenUp的可选过滤器,这个过滤器对于Struts2和其他框架的集成很有帮助,例如:SiteMesh Plugin)
3、接着FilterDispatcher被调用,FilterDispatcher询问ActionMapper来决定这个请求是否需要调用某个Action。FilterDispatcher是控制器的核心,就是mvc中c控制层的核心。
下面粗略的分析下我理解的FilterDispatcher工作流程和原理:FilterDispathcer进行初始化并启用核心doFilter
4、如果ActionMapper决定需要调用某个Action,FilterDispatcher则把请求的处理交给ActionProxy
5.ActionProxy通过ConfigurationManager询问框架的配置文件,找到需要调用的Action类 ,这里,我们一般是从struts.xml配置中读取。
6.ActionProxy创建一个ActionInvocation的实例,同时ActionInvocation通过代理模式调用Action。但在调用之前,ActionInvocation会根据配置加载Action相关的所有Interceptor(拦截器)
7.action执行完毕后,ActionInvocation负责根据struts.xml中的配置找到对应的返回结果result。
Struts 2设计的精巧之处就是使用了Action代理,Action代理可以根据系统的配置,加载一系列的拦截器,由拦截器将 HttpServletRequest参数解析出来,传入Action。
同样,Action处理的结果也是通过拦截器传入 HttpServletResponse,然后由HttpServletRequest传给用户。
Struts2线程安全
Servlet是单例的
Struts2是线程安全的. Struts2的action是多例的,每次请求都会新创建一个新的action对象,
Struts2的action对象是反射生成的,所以需要有一个共公的无参构造方法
Struts2常量配置
默认的配置文件位置:
配置方式一
在struts文件中配置:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<!-- 解决乱码 -->
<constant name="struts.i18n.encoding" value="UTF-8" />
<!-- 自定义请求扩展名 表示用action 或空 或 do结尾都可以-->
<constant name="struts.action.extension" value="action,,do" />
<!-- 开发模式异常友好提示 -->
<constant name="struts.devMode" value="true" />
<!-- 配置文件改变后,自动装载 -->
<constant name="struts.configuration.xml.reload" value="true" />
..
</struts>
配置方式二
在src下添加struts.properties文件
struts.i18n.encoding=UTF-8
struts.action.extension=action,do
注:两种方式选其一
多配置文件
在struts.xml中引用:
<include file="struts-user.xml"></include>
struts-user.xml内容
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.5//EN" "http://struts.apache.org/dtds/struts-2.5.dtd"> <struts> <package name="login" extends="default" namespace="/login"> <action name="login" class="com.ts.action.LoginAction" method="login"> <result name="success">/wel.jsp</result> <result name="fail">/500.jsp</result> </action> <action name="logout" class="com.ts.action.LoginAction" method="logout"> <result name="success">/login.jsp</result> </action> </package> </struts>
|
action的result
Struts配置文件中的action标签的子标签:
result标签有name属性,还有一个type属性
name对应请求处理方法的返回值,默认是success
type是指请求跳转的类型,默认是dispatcher也就是转发
result类型
struts-default配置文件中
|
重点:
1. chain指action链,执行actionA后再执行actionB再执行actionC.几乎不用
<result type=”chain”>logout</result> 表示执行上一个action之后,再执行logout这action
2. dispatcher转发与servlet相同
3. redirect指重定向,与servlet相同
注:<result type=”redirect”>logout.action</result> 表示重定向到logout.action,
注意与chain的区别
4. redirectAction与redirect相同的使用,但不要加后缀
5. Stream:以流的形式显示,用于文件下载
全局结果集<global-results>
<global-results>
<result name=”fail”>/url</result>
</global-results>
需要配置在所有的aciton前面,也就是在<package>下面第一行
表示所有返回fail的执行global-results的配置
default-class-ref
如果action中不指定class那么会由struts2的默认配置struts-default.xml
中default-class-ref所指定的action去执行
<default-class-ref class="com.opensymphony.xwork2.ActionSupport" /> |
<package name="he" extends="default" namespace="/he"> <action name="hello" ><!-- 没有配置class--> <result>/index.jsp</result> </action> </package> |
程序员可以自已配置默认执行的class:
<package name="default" extends="struts-default"> <interceptors> <interceptor name="mytime" class="com.interceptor.TimerInterceptorE" /> <interceptor name="loginCkIntptor" class="com.interceptor.LoginCeckInterceptor" /> <interceptor-stack name="mystack"> <interceptor-ref name="loginCkIntptor"></interceptor-ref> <interceptor-ref name="mytime"></interceptor-ref> <interceptor-ref name="defaultStack"></interceptor-ref> </interceptor-stack> </interceptors> <default-interceptor-ref name="mystack"></default-interceptor-ref> <default-class-ref class="类路径"></default-class-ref> <global-results> <result name="login">/login.jsp</result> </global-results> </package> |
通配置符*
减少action的配置,还可以使用DMI(动态方法调用) 很方便. (官网提出存在安全隐患,不建议使用)
<action name="*" class="com.ts.action.LoginAction" method="{1}"> <result name="success">/login.jsp</result> </action> |
name=*表示,输入的字符,其实就是请求名,与后面的method对应的占位符{1}对应 也就是{1}就是*的内容 |
<action name="user*" class="com.ts.action.LoginAction" method="{1}"> <result name="success">/login.jsp</result> </action> |
表示以user开头的请求都匹配此请求,{1}就是*的内容 |
<action name="user*_*" class="com.ts.action.LoginAction" method="{2}"> <result name="success">/login.jsp</result> </action> |
{2}表示为第二个*号的内容 |
Action三种实现方式
1. 直接定义一个普通类
好处:不具有侵入性
public class HelloStruts { public String execute(){ System.out.println("你好1,struts2"); return "aa"; } }
|
2. 实现Action接口
好处:接口提供了规范,
public class HelloStruts implements Action{ public String execute(){ System.out.println("你好1,struts2"); return "aa"; } }
|
3. 继承ActionSupport类
好处:不强制程序员重写或实现方法,可以使用struts框架提供的验证
public class HelloStruts extends ActionSupport{ public String execute(){ System.out.println("你好1,struts2"); return "aa"; } }
|
对象传值
ModelDrivenInterceptor
属性驱动
用对象的方式处理表单数据
案例:实现注册功能
页面:input name=在action中的对象名.属性名
input name=在action中的属性名
模型驱动
public class UserModelAction implements ModelDriven<User> { private User user = new User(); @Override public User getModel() { return user; } public String register(){ //注册 return "scuess"; } }
|
案例:实现注册功能
页面:input name=属性名
如果实体类的属性比较多,建议使用模型驱动
项目
用struts2实现
获取servletApi
走源码
1. 解耦使得在使用struts2进行测试时,不需要启动服务器
在一定程度上提高了开发效率.
Struts2的执行效率不高,但思想很好,解耦很好
Struts2在MVC中担当是V的角色, 也就是action
2. 通过ActionContext 获取 servletApi
ActionContext.getContext().getSession().put("user",user); HttpServletRequest re=(HttpServletRequest)ActionContext.getContext().get(StrutsStatics.HTTP_REQUEST); ActionContext.getContext().getApplication(); |
3. 通过耦合的方法获取 HttpServletRequest,实现ServletRequestAware
public class UserModelAction2 implements ServletRequestAware { private User user = new User(); HttpServletRequest request; @Override public void setServletRequest(HttpServletRequest httpservletrequest) { this.request = httpservletrequest; } |
4. 通ServletActionContext获取
HttpServletRequest req = ServletActionContext.getRequest(); |
建议使用最后一种 ServletActionContext
ActionContext
1.
2. 获得得ActionContext
ActionContext a = ActionContext.getContext(); |
3. ActionContext流程图
走源码
4. ThreadLocal模式
保证了ActionContext线程安全
public static void main(String[] args) { final ThreadLocal<String> tl = new ThreadLocal<>(); tl.set("aaaa"); new Thread(new Runnable() { @Override public void run() { System.out.println("thread: "+tl.get()); } }).start(); System.out.println("main: "+tl.get()); } |
ThreadLocal该类提供了线程局部 (thread-local) 变量。这些变量不同于它们的普通对应物,因为访问某个变量(通过其 get 或 set 方法)的每个线程都有自己的局部变量,它独立于变量的初始化副本。ThreadLocal 实例通常是类中的 private static 字段,它们希望将状态与某一个线程(例如,用户 ID 或事务 ID)相关联。 |
5. ActionContext包含6大对象
ValueStack(值栈)
值栈ValueStack是ActionContext中的一个对象,它是一个栈,FILO先进后出
Struts2中的值栈存放的数据是Action对象
Name=b
Name=a
程序员也可以操作值栈,也可以往值栈放数据,
但是如果存放的数据名与已有的action属性名相同,那么会取不到先进去的值
所以不建议操作值栈
Ognl操作值栈时屏蔽了action,用OgnlValueStack进行操作。直接把值放进值栈中,直接用key就可以取出值
OGNL表达式
OGNL是Object-Graph Navigation Language的缩写,全称为对象图导航语言,是一种功能强大的表达式语言,它通过简单一致的语法,可以任意存取对象的属性或者调用对象的方法,
能够遍历整个对象的结构图,实现对象属性类型的转换等功能。
OGNL上下文实际上就是一个Map对象,由ognl.OgnlContext类表示。它里面可以存放很多个JavaBean对象。它有一个上下文根对象。
上下文中的根对象可以直接使用名来访问或直接使用它的属性名访问它的属性值。否则要加前缀“#key”。
Struts2的标签库都是使用OGNL表达式来访问ActionContext中的对象数据的。如:<s:property value="xxx"/>。
Struts2将ActionContext设置为OGNL上下文,并将值栈作为OGNL的根对象放置到ActionContext中
值栈(ValueStack) :
可以在值栈中放入、删除、查询对象。访问值栈中的对象不用“#”。
Struts2总是把当前Action实例放置在栈顶。所以在OGNL中引用Action中的属性也可以省略“#”。
调用ActionContext的put(key,value)放入的数据,需要使用#访问。
OGNL中重要的3个符号:#、%、$:
#、%和$符号在OGNL表达式中经常出现,而这三种符号也是开发者不容易掌握和理解的部分,需要时间的积累才渐渐弄清楚……
1.#符号
#符号的用途一般有三种。
— 访问非根对象属性,例如#session.msg表达式,由于Struts 2中值栈被视为根对象,所以访问其他非根对象时,需要加#前缀。实际上,#相当于ActionContext. getContext();#session.msg表达式相当于ActionContext.getContext().getSession(). getAttribute("msg") 。
— 用于过滤和投影(projecting)集合,如persons.{?#this.age>25},persons.{?#this.name=='pla1'}.{age}[0]。
— 用来构造Map,例如示例中的#{'foo1':'bar1', 'foo2':'bar2'}。
%符号的用途是在标志的属性为字符串类型时,计算OGNL表达式的值,这个类似js中的eval,很暴力。
$符号主要有两个方面的用途。
— 在国际化资源文件中,引用OGNL表达式,例如国际化资源文件中的代码:reg.agerange=国际化资源信息:年龄必须在${min}同${max}之间。
— 在Struts 2框架的配置文件中引用OGNL表达式,例如:
<validators> <field name="intb"> <field-validator type="int"> <param name="min">10</param> <param name="max">100</param> <message>BAction-test校验:数字必须为${min}为${max}之间!</message> </field-validator> </field> </validators>
|
Jstl标签是一种标准,主流技术框架都支持。
public static void main(String[] args) throws OgnlException { Map<String,Object> map = new HashMap<String,Object>(); map.put("name", "张三"); User u = new User(); u.setName("历史"); System.out.println(Ognl.getValue("#name", map, u)); } |
Struts2中使用ognl表达式
是通过struts2的标签来实现的。
在ognl中,把ActionContext做为ognl中的内容,取此内容需要在key前加上#
valueStack做为ognl中的根对象,取此内容直接取
使用struts2标签
页面导入struts标签库:
<%@ taglib prefix="s" uri="/struts-tags" %> |
登录成功,欢迎:<s:property value="name"/> |
ActionContext中的值,用标签如何取:
ActionContext.getContext().getSession().put("userName", name); <s:property value="#session.userName"/> 加上#,因为ognl 把ActionContext做为ognl中的内容,取此内容需要在key前加上# |
注:
使用struts2标签,需要通过Struts2的过滤器来启用,
如果过滤器的配置是以.action结尾时,不能直接访问有Struts2标签的jsp页面,需要通action跳转
Struts2中不要直接访问jsp,一定要用action来控制跳转
手动修改值栈
private String name; private String pd; /** * 登录 * @return */ public String login() { if (name.equals("admin") && pd.equals("admin")) { ActionContext.getContext().getSession().put("userName", name); User u = new User(); u.setName("aaaa"); ActionContext.getContext().getValueStack().push(u); return "success"; }else { return "fail"; } } |
登录成功,欢迎:<s:property value="name"/> <a href="login/logout.action">退出登录</a> <br/> <s:property value="#session.userName"/><br> 栈顶0<s:property value="[0].name"/> 1 <s:property value="[1].name"/>
|
结论:
登录后,struts2将name存到了值栈中,
手动将user对象放进值栈中后,name名与之前放的相同,那么后放的name存在于栈顶,
所以<s:property value="name"/>取到的不再是登录的登录名,而是手动添加到值栈中的值,
如果想要得到最开始的登录name就需要到栈底取。这里只有两个值在栈中,所以1就是就是登录的名称
所以不要轻易操作值栈。
OGNL应用
Action和service
public class UsersAction extends ActionSupport { private static final long serialVersionUID = 244495613102527095L; private List<Users> usersList; /** * list * @return */ public String queryAll() { usersList = new UsersService().queryAll(); if (null != usersList) { return "success"; }else { return "fail"; } } public List<Users> getUsersList() { return usersList; } public void setUsersList(List<Users> usersList) { this.usersList = usersList; } } |
public class UsersService{ public List<Users> queryAll() { try { Connection conn = (Connection)DatabaseCon.getMySqlDbCon(); Statement st = conn.createStatement(); String sql = "select * from users "; ResultSet rs = st.executeQuery(sql); List<Users> dataList = new ArrayList<>(); while (rs.next()) { Users users = new Users(); users.setId(rs.getInt(1)); users.setName(rs.getString(2)); users.setAccount(rs.getString(3)); users.setPd(rs.getString(4)); users.setAge(rs.getInt(5)); users.setSex(rs.getInt(6)); users.setTel(rs.getString(7)); users.setMail(rs.getString(8)); users.setAddr(rs.getString(9)); dataList.add(users); } return dataList; } catch (Exception e) { e.printStackTrace(); } return null; }} |
页面点击
<a href="<%=request.getContextPath()%>/users/queryAll">用户</a> |
页面展示
<body> 用户列表 <table border="1"> <tr> <td>编号</td> <td>昵称</td> <td>账号</td> <td>密码</td> <td>年龄</td> </tr> <s:iterator value="usersList"> <tr> <td><s:property value="id"/> </td> <td><s:property value="name"/></td> <td><s:property value="account"/></td> <td><s:property value="pd"/></td> <td><s:property value="age"/></td> </tr> </s:iterator> </table> </body> |
常用标签:
http://jimingsong.iteye.com/blog/1582939
常用标签
(可以在这个下面看:)
<s:if/> ---- if标签 使用:<s:if test=""></s:if>
<s:textfield> ---- 文本输入框 使用:<s:textfield name=”实体Bean。属性”></s:textfield>
<s:textarea> ----- 文本域输入框 使用:<s:textarea name=”实体Bean。属性”></s:textarea>
<s:password> ----- 密码输入框 使用:<s:password name=”实体Bean。属性”></s:password>
<s:radio/> ----单选框
<s:checkbox/> ---- 多选框 使用:<s:checkbox name=”自己随便起” value = “值” > 足球
<s:select/> ---- 下拉框的使用 使用:<s:select label=”请选择” list=”{‘book’, ‘pen’, ‘moon’}” value=”%{‘pen’}”> value : 表示默认值。
<s:iterator />循环:
使用:<s:iterator value="listMenu" />
<c:property value=”实体里面的属性”>
类型转换
在Servlet中,如果表单中提交的数据在后台存储的是数字类型时,那么需要手动转换
String age = request.getParameter("age"); if (null != age) { int ageInt = Integer.valueOf(age); } |
在struts2中,对于常见的数据类型struts2已经自动进行了转换.但某些情况下,需要用到自定义数据类型,struts2不能完成类型转换,那么如果此类型使用频繁可以使用struts2提供的类型转换器进行转换.
重点:
1. 继承StrutsTypeConverter
1. 编写xwork-conversion.properties(名称不能改)放在src下.
内容:
key=value
Key为需要类型转换的类
Value为自定义类型转换器
案例:坐标
需求:在页面输入一个数学的点,也就是坐标,在后台能正常的获取,并能正常的展示
Bean:
public class Point { private String x; private String y; public String getX() { return x; } public void setX(String x) { this.x = x; } public String getY() { return y; } public void setY(String y) { this.y = y; } } |
Action
public class UsersAction extends ActionSupport { public String myType(){ System.out.println(point.getX()+" - "+point.getY()); return "success"; } public Point getPoint() { return point; } public void setPoint(Point point) { this.point = point; } } |
Struts.xml
<package name="users" extends="default" namespace="/users">
<action name="myType" class="com.user.action.UsersAction" method="myType"> <result name="success">/users/myType.jsp</result> <result name="fail">/500.jsp</result> </action> </package> |
页面:
<body> <s:form action="/users/myType.action"> 坐标: <s:textfield name="point" /> <s:submit value="提交"></s:submit> </s:form> 获得结果: <s:property value="point" /> </body> |
类型转换器
public class PointConverter extends StrutsTypeConverter{ @Override public Object convertFromString(Map context, String[] values, Class toClass) { String pointV = values[0]; System.out.println(pointV); Point point = new Point(); point.setX(pointV.split(",")[0]); point.setY(pointV.split(",")[1]); return point; } @Override public String convertToString(Map context, Object o) { Point point = (Point)o; return point.getX()+","+point.getY(); } } |
xwork-conversion.properties
com.user.entry.Point=com.converter.PointConverter |
执行结果:
Struts2的验证
手动验证-服务端验证
1. Action继承ActionSupport,重写validate方法.Action在执行过程中,先执行validateXXX方法,然后在执行validate方法,再执行指定的方法
2. 执行流程
注:如果action的实现方式是执行默认的execute方法,那么只会执行validate方法后再执行execute方法,比以上流程少一个流程
如果action的实现方式是执行指定的方法,那么就符合以上流程
3. 案例:
Action
public class UsersAction extends ActionSupport { private Users users; public String register(){ System.out.println("register 中 age = "+users.getAge()); return SUCCESS; }
@Override public void validate (){ //先执行此验证方法,再执行register方法 if (users.getAge() > 100 || users.getAge() < 1) { this.addActionError("年龄不合法");//添加action基础错误 } if (users.getSex() >= 2 ) { this.addFieldError("sex", "sex不合法"); //添加字段错误提示信息,错误级别为字段 } this.addActionMessage("有异常");//添加action基础错误 } public Users getUsers() { return users; } public void setUsers(Users users) { this.users = users; } } |
页面:
<body> <s:fielderror /><!--一定要写个这个标签或<s:actionerror/>,才可以显示错误提示--> <s:form action="/users/register.action"> <s:textfield name="users.name" label="昵称" /> <s:password name="users.pd" label="密码"/> <s:textfield name="users.age" label="年龄"/> <s:textfield name="users.sex" label="性别" /> <s:submit value="提交"></s:submit> </s:form> </body> |
<body> 登录成功,欢迎:<s:property value="name"/> <a href="login/logout.action">退出登录</a>
<br/> <a href="<%=request.getContextPath()%>/users/queryAll">用户列表</a> <br/> <a href="<%=request.getContextPath()%>/users/register.jsp">用户注册</a> </body> |
Struts.xml
<action name="register" class="com.user.action.UsersAction" method="register"> <result name="success">/users/register.jsp</result> <result name="input">/users/register.jsp</result> <!--一定要配置,验证有问题之后默认返回input,并会提示相应的错误信息--> <result name="fail">/500.jsp</result> </action> |
Struts2验证框架
验证器原理和自定义验证器:
http://blog.csdn.net/coslay/article/details/32179627
验证执行时机:验证发生在execute方法运行之前,在Struts2的params拦截器已经把请求的参数反射的设置到Action的属性之后,所以,验证框架实际上验证的是值栈里面的内容。
验证的结果:如果用户输入的参数完全满足验证条件,则会继续执行execute方法,如果用户输入的参数不满足验证条件,注意:3个验证条件只要有一个验证通不过,就会跳转到这个Action所配置的名为input的Result,所以在struts.xml里最好配置一个名为input的result。
给相应的action添加一个验证文件.文件名为”action全名-validation.xml”,例:UsersValidateAction-validation.xml, 一定要放在与action同一目录下.
field:需要校验的字段,name属性值需要和action中的字段一致;
field-validator:校验器,type属性值为struts2框架提供的校验器。
param:是为type指定的校验器提供参数值,因为一个校验器对应一个java类,可以通过param标签为该类中的字段指定值。
这里<param name="trim">true</param>意思是设置requiredstring校验器所引用的类com.opensymphony.xwork2.validator.validators.RequiredStringValidator中的字段trim的值为true,意思是对name属性值作去前后空格处理。
<message>:用于发送错误信息
<!DOCTYPE validators PUBLIC "-//Apache Struts//XWork Validator 1.0.3//EN" "http://struts.apache.org/dtds/xwork-validator-1.0.3.dtd"> <!-- 引入该头文件 --> <validators> <field name="users.name"> <!--与页面的字段名要相对应--> <field-validator type="requiredstring"> <param name="trim">true</param> <message key="nameValidateStr">昵称必填</message> </field-validator> <field-validator type="stringlength"> <param name="trim">true</param> <param name="minLength">3</param> <param name="maxLength">10</param> <message>昵称长度需要${minLength}到${maxLength}</message> </field-validator> </field> <field name="users.pd"> <field-validator type="required"> <message>密码必填</message> </field-validator> <field-validator type="stringlength"> <param name="min">6</param> <param name="max">10</param> <message>密码长度需要${min}到${max}</message> </field-validator> </field> <field name="users.age"> <field-validator type="int"> <param name="min">0</param> <param name="max">100</param> <message key="age">年龄要在${min}到${max}之间</message> </field-validator> </field> </validators> |
页面:
<body> <s:form action="/usersValidate/add.action"> <s:textfield name="users.name" label="昵称" /> <s:password name="users.pd" label="密码"/> <s:textfield name="users.age" label="年龄"/> <s:textfield name="users.sex" label="性别" /> <s:submit value="提交"></s:submit> </s:form> </body> |
Action 一定要继承ActionSupport
public class UsersValidateAction extends ActionSupport {
private static final long serialVersionUID = 1L; private Users users; /** * 添加 * @return */ public String add(){ System.out.println(users.getName()); return SUCCESS; } public Users getUsers() { return users; } public void setUsers(Users users) { this.users = users; } } |
源码 验证xml文件的field-validator的type的值 ( <field-validator type="requiredstring">) 在文件:
中查找,每个相应的类型都对应相应的验证器
|
效果:
在效验文件里又分为两种:
字段校验:字段用什么校验器来校验。
非字段校验:是用校验器校验什么字段。
通俗点讲:字段校验:校验谁,用什么方法。
非字段校验:用什么校验,校验谁 。
自定义验证器
package com.yirong.validator;
import com.opensymphony.xwork2.ActionSupport;
/**
* Action类
* @author luolinghong
*
*/
public class RegisterAction extends ActionSupport{
private static final long serialVersionUID = 1L;
private String name;
private String password;
private String accou
private Integer age;
@Override
public String execute() throws Exception {
return SUCCESS;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getAccount() {
return account;
}
public void setAccount(String account) {
this.account = account;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
package com.yirong.validator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.opensymphony.xwork2.validator.ValidationException;
import com.opensymphony.xwork2.validator.validators.FieldValidatorSupport;
/**
* 自定义的一个校验器类
* @author luolinghong
*
*/
public class PasswordIntegrityValidator extends FieldValidatorSupport {
private String specialCharacters;
static Pattern digitPattern = Pattern.compile( "[0-9]");
static Pattern letterPattern=
Pattern.compile( "[a-zA-Z]");
static Pattern specialCharsDefaultPattern = Pattern.compile( "!@#$");
public String getSpecialCharacters() {
return specialCharacters;
}
public void setSpecialCharacters(String specialCharacters) {
this.specialCharacters = specialCharacters;
}
@Override
public void validate(Object object) throws ValidationException {
String fieldName = getFieldName();
String fieldValue = (String) getFieldValue(fieldName, object );
fieldValue = fieldValue.trim();
Matcher digitMatcher = digitPattern.matcher(fieldValue);
Matcher letterMatcher = letterPattern.matcher(fieldValue);
Matcher specialCharacterMatcher;
if ( getSpecialCharacters() != null ){
Pattern specialPattern = Pattern.compile("[" + getSpecialCharacters() + "]" );
specialCharacterMatcher = specialPattern.matcher( fieldValue );
} else{
specialCharacterMatcher = specialCharsDefaultPattern.matcher( fieldValue );
}
if ( !digitMatcher.find() ) {
addFieldError( fieldName, object );
}else if ( !letterMatcher.find() ) {
addFieldError( fieldName, object );
}else if ( !specialCharacterMatcher.find() ) {
addFieldError( fieldName, object );
}
}
}
这是字段效验方式
在Action类下面配置一个Action类名-validation.xml的配置文件,内容如下:
<!DOCTYPE validators PUBLIC "-//Apache Struts//XWork Validator 1.0.3//EN"
"http://struts.apache.org/dtds/xwork-validator-1.0.3.dtd">
<validators>
<field name="account">
<field-validator type="requiredstring">
<message>请输入账号</message>
</field-validator>
</field>
<field name="name">
<field-validator type="requiredstring">
<message>请输入姓名</message>
</field-validator>
</field>
<field name="age">
<field-validator type="int">
<param name="min">18</param>
<message>年龄必须在18岁以上</message>
</field-validator>
</field>
<field name="password">
<field-validator type="passwordintegrity">
<param name="specialCharacters">$!@#?</param>
<message>您的密码必须包括至少一个数字,字符,并包括如下的特殊的字符: "${specialCharacters}"</message>
</field-validator>
</field>
</validators>
非字段效验方式:
<validators>
<validator type="requiredstring">
<param name="filedName">userName</param>
<message>名字不能为空</message>
</validator>
<validator type="passwordintegrity">
<param name="filedName">password</param>
<message>错误信息</message>
</validator>
</validators>
在src下面创建一个validators.xml文件,里面内容为:
(里面的内容可以在xwork的xwork-core-2.3.24.1.jar 下com.opensymphony.xwork2 下 validator 下的validators里面有个default.xml文件里面去找,然后在把自己的验证器加进去 )
Register.jsp注册页面:
<%@ page language="java" contentType="text/html; charset=UTF-8" %>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>Struts2验证框架</title>
</head>
<body>
<s:form action="/register/register.action" >
<s:textfield name="account" label="账号" />
<s:password name="password" label="密码" />
<s:textfield name="name" label="姓名" />
<s:textfield name="age" label="年龄" />
<s:submit value="注册" />
</s:form>
</body>
</html>
验证结果:
拦截器
什么是拦截器、拦截器栈
http://www.cnblogs.com/felix-/p/4319798.html (拦截器的原理分析)
在访问类的某个方法或者属性之前执行,拦截的是Action的请求,进行拦截然后在方法的执行前或者之后加入某些操作
大部分时候,拦截器方法都是通过代理的方式来调用的。Struts 2的拦截器实现相对简单。当请求到达Struts 2的ServletDispatcher时,Struts 2会查找配置文件,并根据其配置实例化相对的拦截器对象,然后串成一个列表(list),最后一个一个地调用列表中的拦截器。struts2自带有拦截器,当然我们也可以自己定义自己的拦截器,需要实现interceptor这个接口,然后在里面定义你需要拦截的页面或是请求,或是其他的!当然仅仅这样还是不行,还需要在struts.xml里面配置拦截器,这样当你发送请求的时候就会先执行拦截器里面的东西了!
http://blog.csdn.net/u010214269/article/details/47362331 (AOP和OOP的理解)
http://blog.csdn.net/Intlgj/article/details/5671248
AOP(Aspect-Oriented Programming,面向方面编程),可以说是OOP(Object-Oriented Programing,面向对象编程)的补充和完善.
AOP技术是建立在Java语言的反射机制与动态代理机制之上的,它利用一种称为“横切”的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其名为 “Aspect”,即方面。所谓“方面”,简单地说,就是将那些与业务无关,却为业务模块所共同,便于减少系统的重复代码,降低 模块间的耦合度,并有利于未来的可操作性和可维护性。AOP代表的是一个横向的关系,如果说“对象”是一个空心的圆柱体,其中封装的是对象的属性和行为; 那么面向方面编程的方法,就仿佛一把利刃,将这些空心圆柱体剖开,以获得其内部的消息。而剖开的切面,也就是所谓的“方面”了。然后它又以巧夺天功的妙手 将这些剖开的切面复原,不留痕迹。
结论:
拦截器和过滤器相类似.在action的执行前后执行.Struts2的核心功能都是通过拦截器实现的.
程序员可以自定义拦截器,
Struts2的方便之处就是因为需要某些功能可以自己去定义去配置,比如struts2的验证框架、类型转换器
拦截器栈:由多个拦截器组成
拦截器原理
在提交数据到框架时,框架调用拦截器的过程,首先框架会根据URL请求创建指定的动作TestAction,将 TestAction的实例和TestAction相关的拦截器引用myStack放入一个新的ActionInvocation对象中(还包含其他信 息),然后框架调用ActionInvocation的invoke()方法,此时开始了拦截器栈调用过程,最开始调用拦截器栈的第一个拦截器也就是 Intercept1,拦截器执行完预处理后,因为intercept()方法接收一个ActionInvocation对象作为参数,在 Intercept1.intercept()方法中继续调用 ActionInvocation对象的invoke()方法将向下继续调用栈中余下的拦截器Intercept2...一直到栈中没有拦截器为止,最后 执行动作组件。在结果被呈现之后,拦截器会按照相反的顺序再触发一遍,使他们可以进行后处理。
struts-default.xml:
拦截器和过滤器的区别
多个拦截器之间是用责任链模式来实现的,拦截器执行流程:
自定义拦截器(实现拦截器)
1. 编写拦截器,实现Interceptor接口或者继承AbstractInterceptor类,
2. 在struts.xml文件中配置拦截器
3. 在action中引用拦截器
Action
public class LoginAction extends ActionSupport { private String name; private String pd;
/** * 登录 * @return */ public String login() { if ("admin".equals(name) && "admin".equals(pd)) { ActionContext.getContext().getSession().put("userName", name); User u = new User(); u.setName("aaaa"); ActionContext.getContext().getValueStack().push(u); return "success"; }else { return "fail"; } } public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getPd() { return pd; }
public void setPd(String pd) { this.pd = pd; } } |
拦截器
public class TimerInterceptorE extends AbstractInterceptor{ @Override public String intercept(ActionInvocation invocation) throws Exception { long starttime = System.currentTimeMillis(); String result = invocation.invoke();//执行程序编写的action System.out.println(invocation.getAction().toString()+"执行时长:"+(System.currentTimeMillis()-starttime)+""); return result; } } |
Struts.xml配置
<package name="login" extends="default" namespace="/login"> <interceptors> <interceptor name="mytime" class="com.interceptor.TimerInterceptorE" /> </interceptors> <action name="login" class="com.ts.action.LoginAction" method="login"> <result name="success">/wel.jsp</result> <result name="fail">/500.jsp</result> <!--在action中引用拦截器--> <interceptor-ref name="mytime"></interceptor-ref> </action> </package> |
问题:登录的action中得不到值了。
结论:使用了自定义拦截器之后,默认的拦截器将会失效,也就无法取得页面的值。想要有自定义拦截器,以希望要struts2的默认拦截器生效,那么需要手动配置
改以上配置为:
Struts.xml配置
<package name="login" extends="default" namespace="/login"> <interceptors> <interceptor name="mytime" class="com.interceptor.TimerInterceptorE" /> </interceptors> <action name="login" class="com.ts.action.LoginAction" method="login"> <result name="success">/wel.jsp</result> <result name="fail">/500.jsp</result> <!--在action中引用拦截器--> <interceptor-ref name="mytime"></interceptor-ref> <!- 引用struts2的默认拦截器--> <interceptor-ref name="defaultStack"></interceptor-ref> </action> </package> |
当使用多个拦截器时,可以将配置以拦截器栈的方式配置:
<package name="login" extends="default" namespace="/login"> <interceptors> <interceptor name="mytime" class="com.interceptor.TimerInterceptorE" /> <interceptor-stack name="mystack"> <interceptor-ref name="mytime"></interceptor-ref> <interceptor-ref name="defaultStack"></interceptor-ref> </interceptor-stack> </interceptors> <action name="login" class="com.ts.action.LoginAction" method="login"> <result name="success">/wel.jsp</result> <result name="fail">/500.jsp</result> <!--在action中引用拦截器--> <interceptor-ref name="mystack"></interceptor-ref> </action> </package> |
当所有的action都需要使用到自定义的拦截器时,可以将自己的拦截器配置为默认的拦截器,这样不用在每个action中引用也会默认使用
附:中可以查看相应的配置编写的位置
(interceptor是放在package节点下)
<package name="login" extends="default" namespace="/login"> <interceptors> <interceptor name="mytime" class="com.interceptor.TimerInterceptorE" /> <interceptor-stack name="mystack"> <interceptor-ref name="mytime"></interceptor-ref> <interceptor-ref name="defaultStack"></interceptor-ref> </interceptor-stack> </interceptors> <default-interceptor-ref name="mystack"></default-interceptor-ref> <action name="login" class="com.ts.action.LoginAction" method="login"> <result name="success">/wel.jsp</result> <result name="fail">/500.jsp</result>
</action> </package> |
拦截器的应用-验证是否登录
Action拦截器拦截的是action
实现拦截器
/** * 登录验证拦截器实现 */ public class LoginCeckInterceptor extends AbstractInterceptor{ /** * 拦截除登录以外的所有的请求 * 判断是否登录,如果没登录就返回跳转去登录页面的标识 *在intercept方法中这个方法被ActionInvocation.invoke()方法递归调用 */ @Override public String intercept(ActionInvocation invocation) throws Exception { String acctionName = invocation.getProxy().getActionName(); if ("login".equals(acctionName)) { return invocation.invoke(); //invocation.invoke()表示执行程序员提供的action,一定要注意invocation.invoke()写的位置 } Object userName = invocation.getInvocationContext().getSession().get("userName"); if (null == userName) { return Action.LOGIN; } return invocation.invoke(); } } |
Interceptor获得的参数和方法:
ActionInvocation invocation;
1.Action: nvocation.getAction().getClass().getName());
2.Struts2中配置的Action: invocation.getProxy().getActionName());
3.调用的方法: invocation.getProxy().getMethod());
4. HttpServletRequest request = ServletActionContext.getRequest();
ActionContext context = invocation.getInvocationContext();
String path = request.getRequestURI(); //获取当前的url
String reqPamrs = request.getQueryString(); //获得?后面的参数
Struts.xml
<package name="default" extends="struts-default"> <interceptors> <interceptor name="mytime" class="com.interceptor.TimerInterceptorE" /> <interceptor name="loginCkIntptor" class="com.interceptor.LoginCeckInterceptor" /> <!--配置拦截器--> <interceptor-stack name="mystack"> <interceptor-ref name="loginCkIntptor"></interceptor-ref> <!--使用拦截器--> <interceptor-ref name="mytime"></interceptor-ref> <interceptor-ref name="defaultStack"></interceptor-ref> </interceptor-stack> </interceptors> <default-interceptor-ref name="mystack"></default-interceptor-ref> <global-results> <!--配置全局的返回结果跳转--> <result name="login">/login.jsp</result> </global-results> </package> |
登录action
public class LoginAction extends ActionSupport { private String name; private String pd;
/** * 登录 * @return */ public String login() { ActionContext.getContext().getSession().put("userName", name); return Action.SUCCESS; } public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getPd() { return pd; }
public void setPd(String pd) { this.pd = pd; } } |
Struts2全局拦截器配置
方法拦截器
方法拦截器比action拦截器更细粒度,方法拦截器需要继承MethodFilterInterceptor类并重写doIntercept方法
MethodFilterInterceptor类本继承了AbstractInterceptor类
注意:如果一个方法同时在excludeMethods和includeMethods中出现,则该方法会被拦截,以includeMethods参数指定的取胜.
对添加,修改,删除方法进行拦截,验证是否登录
Struts.xml配置:
<package name="default" extends="struts-default"> <interceptors> <interceptor name="methodIntptor" class="com.interceptor.MethodInterceptor" /> <interceptor-stack name="mystack"> <interceptor-ref name="methodIntptor"> <param name="excludeMethods">list</param> <!--配置不需要被拦截 的方法--> <param name="includeMethods">add,update,delete</param><!-- 配置哪些方法需要被拦截 --> </interceptor-ref> <interceptor-ref name="defaultStack"></interceptor-ref> </interceptor-stack> </interceptors> <default-interceptor-ref name="mystack"></default-interceptor-ref> </package>
<package name="he" extends="default" namespace="/he"> <action name="add" class="com.ts.action.HelloStruts" method="add" > <result>/index.jsp</result> <result name="login">/login.jsp</result> </action> <action name="list" class="com.ts.action.HelloStruts" method="list" > <result>/index.jsp</result> </action> <action name="update" class="com.ts.action.HelloStruts" method="update" > <result>/index.jsp</result> <result name="fail">/500.jsp</result> </action> <action name="delete" class="com.ts.action.HelloStruts" method="delete" > <result>/index.jsp</result> <result name="fail">/500.jsp</result> </action> </package> |
方法拦截器的实现:
public class MethodInterceptor extends MethodFilterInterceptor {
/** * 3 */ private static final long serialVersionUID = 1L;
@Override protected String doIntercept(ActionInvocation invocation) throws Exception { Object userName = invocation.getInvocationContext().getSession().get("userName"); if (null == userName) { return Action.LOGIN; } return invocation.invoke(); } } |
Action类:
public class HelloStruts extends ActionSupport {
private static final long serialVersionUID = 244495613102527095L;
public String list(){ System.out.println("list方法"); return SUCCESS; } public String update(){ System.out.println("update方法"); return SUCCESS; } public String add(){ System.out.println("add方法"); return SUCCESS; } public String delete(){ System.out.println("delete方法"); return SUCCESS; } } |
拦截结果监听器
文件上传
Struts2本身有一个文件上传的拦截器,也就是说struts2的文件上传功能就是用拦截器实现的
看源码
页面
<%@ page language="java" contentType="text/html; charset=utf-8"%> <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>fileupload</title> </head> <body> <s:form action="file/fileupload" method="POST" enctype="multipart/form-data"> <s:file name="file" label="文件上传" /> <s:submit value="提交"></s:submit> </s:form> </body> </html> |
Action
public class UploadAction extends ActionSupport { /** * */ private static final long serialVersionUID = 1L; private File file; private String fileFileName; //文件名称 private String fileContentType; //文件类型 public String upload() throws Exception{ HttpServletRequest request = ServletActionContext.getRequest(); /*Set<String> set = request.getServletContext().getResourcePaths("/fileupload"); String path = request.getContextPath() + "/fileupload";*/ InputStream is = new FileInputStream(file); InputStreamReader isr = new InputStreamReader(is); BufferedReader br = new BufferedReader(isr); FileWriter fw = new FileWriter(new File("E:\\eduwkspace\\ts23\\WebContent\\fileupload",fileFileName)); BufferedWriter bw = new BufferedWriter(fw); String line = null; while((line = br.readLine()) != null){ bw.write(line); bw.flush(); } bw.close(); fw.close(); br.close(); isr.close(); is.close(); return Action.SUCCESS; } public File getFile() { return file; } public void setFile(File file) { this.file = file; } public String getFileFileName() { return fileFileName; } public void setFileFileName(String fileFileName) { this.fileFileName = fileFileName; } public String getFileContentType() { return fileContentType; } public void setFileContentType(String fileContentType) { this.fileContentType = fileContentType; } } |
Struts.xml
<package name="file" extends="default" namespace="/file"> <action name="fileupload" class="com.fileupload.UploadAction" method="upload"> <result>/wel.jsp</result> </action> </package> |
文件保存路径
这里指定放在某个目录即可,项目部署后路径是不会随便变动的 |
自己实现的文件上传
http://blog.csdn.net/zhang_xinxiu/article/details/25167207 (文件上传和下载文件)
Action类
package com.yirong.file;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionSupport;
public class FileAction extends ActionSupport {
private static final long serialVersionUID = 1L;
private File file; //文件名称
private String fileFileName; //文件类型
private String fileContentType; //注意:文件名称和文件类型的名称前缀必须相同,
public File getFile() {
return file;
}
public void setFile(File file) {
this.file = file;
}
public String getFileFileName() {
return fileFileName;
}
public void setFileFileName(String fileFileName) {
this.fileFileName = fileFileName;
}
public String getFileContentType() {
return fileContentType;
}
public void setFileContentType(String fileContentType) {
this.fileContentType = fileContentType;
}
/**
* 文件上传
* @return success
* @throws Exception
*/
public String fileLoad() throws Exception{
//获取需要上传文件的文件路径 ,Web应用程序根目录的路径,就可以构建出定位资源的绝对路径。
/*File uploadFile=new File(ServletActionContext.getServletContext().getRealPath("uploadFile"));
System.out.println(uploadFile+"文件上传位置:"); */
File uploadFile=new File("E:\\struts");
//判断文件是否上传,如果上传的话将会创建该目录
if(!uploadFile.exists()){
uploadFile.mkdir(); //创建该目录
}
//声明文件输入流,为输入流指定文件路径
FileInputStream input=new FileInputStream(file);
//获取输出流,获取文件的文件地址及名称
FileOutputStream out=new FileOutputStream(uploadFile + "\\" +fileFileName);
try{
byte[] b=new byte[1024]; //每次写入的大小
int i=0;
while((i=input.read(b))>0){
out.write(b,0,i);
}
System.out.println("文件上传成功");
}catch(Exception e){
e.printStackTrace();
}finally{
input.close();
out.close();
}
return Action.SUCCESS;
}
}
批量文件上传
页面:
<script type="text/javascript" src="/ts23/js/jquery-1.10.2.js"></script> <script type="text/javascript"> $(function (){ $('#btn').click(function (){ var newFileInput = '<div>文件上传:<input type="file" name="file" />'+ '<input type="button" value="删除" οnclick="removeFileInput(this);"/></div>'; $('#addFileInput').append(newFileInput); }); }); function removeFileInput(obj){ $(obj).parent().remove('div'); } </script> <body> <s:actionerror/> <s:form action="file/batchfileupload" method="POST" enctype="multipart/form-data" theme="simple"> 文件上传:<input type="file" name="file" /><input type="button" value="添加" id="btn" /> <div id="addFileInput"></div> <s:submit value="提交"></s:submit> </s:form> </body> |
批量上传Action
public class BatchUploadAction extends ActionSupport { private static final long serialVersionUID = 1L; private File[] file; private String[] fileFileName; // 文件名称 private String[] fileContentType; // 文件类型
public String batchUpload() throws Exception { //HttpServletRequest request = ServletActionContext.getRequest(); for (int i = 0; i < file.length; i++) { InputStream is = new FileInputStream(file[i]); InputStreamReader isr = new InputStreamReader(is); BufferedReader br = new BufferedReader(isr); //文件上传的保存路径指定即可 FileWriter fw = new FileWriter(new File("E:\\eduwkspace\\ts23\\WebContent\\fileupload", fileFileName[i])); BufferedWriter bw = new BufferedWriter(fw); String line = null; while ((line = br.readLine()) != null) { bw.write(line); bw.newLine(); bw.flush(); } bw.close(); fw.close(); br.close(); isr.close(); is.close(); } return Action.SUCCESS; }
public File[] getFile() { return file; }
public void setFile(File[] file) { this.file = file; }
public String[] getFileFileName() { return fileFileName; }
public void setFileFileName(String[] fileFileName) { this.fileFileName = fileFileName; }
public String[] getFileContentType() { return fileContentType; }
public void setFileContentType(String[] fileContentType) { this.fileContentType = fileContentType; }
} |
Struts.xml
<package name="file" extends="default" namespace="/file"> <action name="batchfileupload" class="com.fileupload.BatchUploadAction" method="batchUpload"> <result>/wel.jsp</result> <!--添加input,如有错误则可以正常跳转--> <result name="input">/500.jsp</result> <!--可以使用struts2的拦截器做大小限制,也可以不使用,如若使用,则一定要加默认拦截器--> <interceptor-ref name="fileUpload"> <param name="maximumSize">20971520</param> </interceptor-ref> <interceptor-ref name="defaultStack"></interceptor-ref> </action> </package> |
自己写的批量上传
Action类
package com.yirong.file;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionSupport;
public class FileLoadAction extends ActionSupport{
private static final long serialVersionUID = 1L;
private File[] file;
private String[] fileFileName; //文件类型
private String[] fileContentType; //注意:文件名称和文件类型的名称前缀必须相同,
public File[] getFile() {
return file;
}
public void setFile(File[] file) {
this.file = file;
}
public String[] getFileFileName() {
return fileFileName;
}
public void setFileFileName(String[] fileFileName) {
this.fileFileName = fileFileName;
}
public String[] getFileContentType() {
return fileContentType;
}
public void setFileContentType(String[] fileContentType) {
this.fileContentType = fileContentType;
}
/**
* 多个文件的上传
* @return
* @throws Exception
*/
public String fileLoad() throws Exception{
ServletActionContext.getRequest().setCharacterEncoding("UTF-8");
//取得需要上传的文件数组
File[] files = getFile();
if (files !=null && files.length > 0) {
for (int i = 0; i < files.length; i++) {
//建立上传文件的输出流, getImageFileName()[i]
FileOutputStream fos = new FileOutputStream("E:\\struts"+ "\\" + getFileFileName()[i]);
//建立上传文件的输入流
FileInputStream fis = new FileInputStream(files[i]);
byte[] buffer = new byte[1024];
int len = 0;
while ((len=fis.read(buffer))>0) {
fos.write(buffer, 0, len);
}
fos.close();
fis.close();
}
}
return Action.SUCCESS;
}
}
文件下载
servlet文件下载
Web.xml
<servlet> <servlet-name>downloadServlet</servlet-name> <servlet-class>com.servlet.DownloadServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>downloadServlet</servlet-name> <url-pattern>/downloadServlet.do</url-pattern> </servlet-mapping> |
DownloadServlet:
public class DownloadServlet extends HttpServlet { private static final long serialVersionUID = 1L;
@Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request,response); } @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String fullFilePath = "C:\\Users\\linjuanjuan111\\Desktop\\test\\内存.txt"; /*读取文件*/ File file = new File(fullFilePath); /*如果文件存在*/ if (file.exists()) { response.reset(); response.setContentType("text/html;charset=utf-8"); //中文乱码问题 response.setContentType("application/x-msdownload");//设置为文件下载 response.setHeader("Content-Disposition", "attachment;filename="+ new String(file.getName().getBytes("utf-8"),"ISO-8859-1")); //解决下载文件时中文不显示的问题,因为:HTTP头部的默认编码为ISO-8859-1 int fileLength = (int) file.length(); response.setContentLength(fileLength); /*如果文件长度大于0*/ if (fileLength != 0) { /*创建输入流*/ InputStream inStream = new FileInputStream(file); byte[] buf = new byte[4096]; /*创建输出流*/ ServletOutputStream servletOS = response.getOutputStream(); int readLength; while (((readLength = inStream.read(buf)) != -1)) { servletOS.write(buf, 0, readLength); } inStream.close(); servletOS.flush(); servletOS.close(); } } } } |
效果:
|
用struts2请求下载文件
用Struts2进行文件下载重点:action没有返回结果值,需要返回null
DownloadAction:
public class DownloadAction extends ActionSupport { private static final long serialVersionUID = 1L;
public String download() throws Exception { HttpServletResponse response = (HttpServletResponse)ActionContext.getContext().get(StrutsStatics.HTTP_RESPONSE); String fullFilePath = "C:\\Users\\linjuanjuan111\\Desktop\\test\\内存.txt"; /*读取文件*/ File file = new File(fullFilePath); /*如果文件存在*/ if (file.exists()) { response.reset(); response.setContentType("text/html;charset=utf-8"); //中文乱码问题 response.setContentType("application/x-msdownload"); response.setHeader("Content-Disposition", "attachment;filename=struts2"+ new String(file.getName().getBytes("utf-8"),"ISO-8859-1")); //解决下载文件时中文不显示的问题,因为:HTTP头部的默认编码为ISO-8859-1 int fileLength = (int) file.length(); response.setContentLength(fileLength); /*如果文件长度大于0*/ if (fileLength != 0) { /*创建输入流*/ InputStream inStream = new FileInputStream(file); byte[] buf = new byte[4096]; /*创建输出流*/ ServletOutputStream servletOS = response.getOutputStream(); int readLength; while (((readLength = inStream.read(buf)) != -1)) { servletOS.write(buf, 0, readLength); } inStream.close(); servletOS.flush(); servletOS.close(); } } return null; } } |
Struts2.xml
<package name="file" extends="default" namespace="/file"> <action name="download" class="com.filedownload.DownloadAction" method="download"> <!-- 没有result --> </action> </package> |
效果:
|
Struts2内置的stream文件下载
Struts2框架提供了StreamResult处理文件下载
页面
<body> <s:a href="file/streamDownload?fileName=内存.txt">struts2的stream下载"内存.txt"</s:a> </body> |
Struts.xml
<package name="file" extends="default" namespace="/file"> <action name="streamDownload" class="com.filedownload.StreamDownloadAction" method="streamDownload"> <result type="stream"> <!--<param name="inputName">inputStream</param>这里是什么值,就需要在action中提供相应的get方法, 用于获取流 ,默认是inputStream ,执行完action请求的相应方法之后,就会执行此get方法--> <param name="inputName">aaa</param> <param name="contentType">application/x-msdownload</param> <!-- application/octet-stream;charset=ISO8859-1 表示为文件下载,可不配 --> <param name="contentDisposition">attachment;fileName=${fileName}</param> <!-- 跳出下载的对话框 ${fileName} 表示页面下载框显示的文件名,从action中取 --> </result> </action> </package> |
Action
public class StreamDownloadAction extends ActionSupport { private static final long serialVersionUID = 1L; private String fileName; public String streamDownload(){ return SUCCESS; } /** * 根据xml的inputName参数名提供相应的getXxx方法,用于获取文件流,此方法返回的值由struts2的StreamResult类处理 * @return * @throws Exception */ public InputStream getAaa() throws Exception { return new FileInputStream(new File("C:\\Users\\linjuanjuan111\\Desktop\\test\\",fileName)); } public String getFileName() throws UnsupportedEncodingException { return new String(fileName.getBytes(), "ISO8859-1"); //解决下载文件时中文不显示的问题,要转回为ISO8859-1,因为http载默认编码为ISO8859-1 } public void setFileName(String fileName) throws UnsupportedEncodingException { this.fileName = new String(fileName.getBytes("ISO-8859-1"),"utf-8"); //中文从页面到action时会是乱码,在这里要转为utf-8 } } |
Struts2对数据的批量
Struts2将页面的数据封装到action中的过程中,对数据的封装处理的很完善,以下看看对checkbox的封装,可以是数组、也可以是list,也可以是String
页面:
<form action="/ts23/acount/acountadd.action" method="post"> 爱好 <input type="checkbox" name="boll" value="lq">篮球 <input type="checkbox" name="boll" value="ppq">乒乓球 <input type="checkbox" name="boll" value="zq">足球 <input type="checkbox" name="boll" value="ymq">羽毛球 <br/> 游戏 <input type="checkbox" name="game" value="xxl">消消乐 <input type="checkbox" name="game" value="lol">LOL <input type="checkbox" name="game" value="dota">dota <input type="checkbox" name="game" value="sg">三国 <br/> 爱好的城市: <input type="checkbox" name="hol" value="bj">北京 <input type="checkbox" name="hol" value="sz">深圳 <input type="checkbox" name="hol" value="sh">上海 <input type="checkbox" name="hol" value="gz">广州 <br/> <input type="submit" value="提交"/> </s:form> |
Action:
public class AcountAction extends ActionSupport { private String boll; private String game[]; private List<String> hol; public String add(){ System.out.println("爱好:"+boll); for (int i = 0; i < game.length; i++) { System.out.println("游戏:"+game[i]); } for (int i = 0; i < hol.size(); i++) { System.out.println("爱好的城市:"+hol.get(i)); } return SUCCESS; }
public String getBoll() { return boll; } public void setBoll(String boll) { this.boll = boll; } public List<String> getHol() { return hol; } public void setHol(List<String> hol) { this.hol = hol; } public String[] getGame() { return game; } public void setGame(String[] game) { this.game = game; } } |
Struts.xml:这里用到的通配符的方式,建议不使用
<package name="acount" extends="default" namespace="/acount"> <action name="acount*" class="com.user.action.AcountAction" method="{1}"> <result name="success">/wel.jsp</result> <result name="fail">/500.jsp</result> </action> </package> |
效果:struts2默认以,隔开封装到String类型的属性中。如果数组,list,Struts2一样可以正确的将值封装到相应的属性中。
|
|
Ajax支持
Struts2 支持 servlet ajax
在struts2中一样可以使用servlet API的方式来实现ajax。
结合之前所学的知识点,其实struts2框架只是把很多功能集成、封装在一起。
例:
页面:
<%@ page language="java" contentType="text/html; charset=utf-8"%> <html> <head> <title>ajax</title> </head> <script type="text/javascript" src="/ts23/js/jquery-1.10.2.js"></script> <script type="text/javascript"> $(function(){ $('#btn').click(function(){ $.post('/ts23/ajax/ajax',function(data){ $('#msg').html(data); }); }); }); </script> <body> <input type="button" id="btn" value="sruts2 ajax"> <div id="msg"> </div> </body> </html> |
Action
public class AjaxAction extends ActionSupport { private static final long serialVersionUID = 1L;
public String myAjax() throws Exception { HttpServletResponse response = ServletActionContext.getResponse(); response.getWriter().write("sruts2 ajax"); return null; //返回null } } |
Struts.xml
<package name="myajax" extends="default" namespace="/ajax"> <action name="ajax" class="com.ajax.AjaxAction" method="myAjax"> <!-- 没有result --> </action> </package> |
效果:
|
Sruts2 json
Json的全称是JavaScript Object Notation,即JavaScript对象符号,它是一种轻量级的数据交换格式,它是一种与语言无关的数据交换格式,这一点与xml类似。Json主要有两种数据结构:
1. 由key-value组成的数据结构,这种结构在不同的语言中有不同实现。在java中是map,在javaScript中是对象
2. 有序集合结构,比如list,array,map
使用struts2的json,需要倒入相关的jar包
例:重点已用红色标出
导入jar包:jar包在struts2的all.zip/lib中获取
ezmorph-1.0.6.jar json-lib-2.3-jdk15.jar struts2-json-plugin-2.3.24.1.jar org.apache.commons.lang.jar |
Struts.xml
<package name="myajax" extends="json-default" namespace="/ajax"> <action name="json" class="com.ajax.Struts2JsonAction" method="myJson"> <result name="success" type="json"> <param name="root">dataMap</param> <!--表示将dataMap对象返回到页面,在action中要提供相应的get方法,执行完action请求的相应方法之后,就会执行此get方法--> </result> </action> </package> |
Action
public class Struts2JsonAction { private Map<String,Object> dataMap; public String myJson() throws Exception { List<Users> list = new ArrayList<>(); Users use = new Users(); use.setName("张三"); use.setAddr("aa"); Users use1 = new Users(); use1.setName("张五"); use1.setAddr("bbb"); list.add(use); list.add(use1); dataMap = new HashMap<String, Object>(); dataMap.put("dataMap",list); dataMap.put("success", true); return "success"; } public Map<String, Object> getDataMap() { return dataMap; }
public void setDataMap(Map<String, Object> dataMap) { this.dataMap = dataMap; } } |
页面:
<%@ page language="java" contentType="text/html; charset=utf-8"%> <html> <head> <title>用户列表</title> </head> <script type="text/javascript" src="/ts23/js/jquery-1.10.2.js"></script> <script type="text/javascript"> $(function(){ $('#btn').click(function(){ $.post('/ts23/ajax/json',function(data){ var html= ''; $.each(data.dataMap,function(i){ html += '姓名:'+data.dataMap[i].name+', 地址:'+data.dataMap[i].addr + '<br />'; }); $('#msg').html(html); },'json'); }); }); </script> <body> <input type="button" id="btn" value="sruts2 json"> <div id="msg"> </div> </body> </html> |
Struts2 i18n国际化
当软件走向国际化时,显示的文字也需要国际化,并且这是一个项庞大的工程.资源文件命名规范为:basename_language_country.properties
JAVA国际化
如果系统中同时存在资源文件、类文件,系统将以类文件为主,而不会调用资源文件。
对于简体中文的Locale,ResourceBundle搜索资源的顺序是:
(1)baseName_zh_CN.class
(2)baseName_zh_CN.properties
(3)baseName_zh.class
(4)baseName_zh.properties
(5)baseName.class
(6)baseName.properties
struts2提供了相应的处理方式,并且方式多种,它是建立在java国际化的基础之上的.一样也是通过提供不同国家/语言环境的消息资源,然后通过ResourceBundle加载指定Locale对应的资源文件,再取得该资源文件中指定key对应的消息.整个过程与JAVA程序的国家化完全相同只是struts2框架对java程序国际化进行了进一步封装,简化了操作.
Struts2的资源文件包括全局资源文件,包范围资源文件,action范围资源文件.范围依次缩小.当三者同时存在时优先使用范围最小的action范围资源文件,如果通过key在action范围资源文件中找不到则依次往大一个范围的资源文件中查找.如若最终找不到相应的key对应的国际化资源,则直接显示key的名字.
native2ascii处理编码
cmd进到需要转码的文件所在的目录 将中文转为Unicode编码并输出到文件: native2ascii 中文内容文件名 目标文件名 将中文转为Unicode编码并输出到控制台: native2ascii 中文内容文件名 将Unicode编码转为中文输出到文件: native2ascii -reverse 中文内容文件名 目标文件名 将Unicode编码转为中文输出到控制台: native2ascii -reverse 中文内容文件名 将文件转为相应的编码输出到文件: 格式: native2ascii -encoding 编码方式 要转换的文件名 目标文件名 例: native2ascii -encoding ISO8859-1 要转换的文件名 目标文件名 |
全局资源文件加载
需要配置相应的国际化资源文件,并在xml中引用,在jsp页面使用struts2提供的相应标签来引用国际化资源,在action中使用struts2提供的相应方法来获取国际化资源.
首先配置资源文件:一定要在src下配置,资源文件名可随意起.(当然要规范)
messageResource.properties(这里的命名省略了_language_country)
loginScuess = \u767B\u5F55\u6210\u529F\uFF01\u6B22\u8FCE{0} 用native2ascii工具处理后可见中文内容: loginScuess = 登录成功!欢迎{0} |
配置struts.xml
<!-- 国际化资源文件 name是常量值,value是资源文件名 --> <constant name="struts.custom.i18n.resources" value="messageResource" /> <!-- 登录 --> <package name="login" extends="default" namespace="/login"> <action name="login" class="com.ts.action.LoginAction" method="login"> <result name="success">/wel.jsp</result> <result name="fail">/500.jsp</result> </action> </package> |
在jsp页面获取:
<body> <form action="login/login.action" method="post"> 登录名:<input name="name" type="text" /><br> 密码:<input name="pd" type="password" /><br> <input type="submit" value="登录" /> </form> </body> |
<body> 登录成功,欢迎:<s:property value="name"/> <br/> 国际化资源文件中取值: <br/> <s:text name="loginScuess" /> <!--表单元素获得国际化资源文件内容,在标签上加上key属性即可 <s:textfield name="users.name" label="昵称" key="loginScuess" />--> </body> |
效果:页面
|
在action中获取:
public class LoginAction extends ActionSupport { private String name; private String pd; public String login() { String lmsg = getText("loginScuess"); System.out.println(lmsg); } //省略未写出 name和pd的get和set方法 } |
效果:action中System.out.println()
|
789101112
4800+925
占位符的处理
在上面的例子中资源文件中有一个{0},这就是占位符,在使用的过程中可以传递参数
在jsp中使用时参数传递的方式:
<body> 登录成功,欢迎:<s:property value="name"/> <br/> 国际化资源文件中取值: <br/> <s:text name="loginScuess"> <s:param> <s:property value="name"/> <!--如果有多个占位符,则在这里写多个参数值--> </s:param> </s:text><br/> </body> |
在action中使用参数传递:
public class LoginAction extends ActionSupport { private String name; private String pd; public String login() { //注意的是这里是数组,而在资源文件中的{0}则表示取数组下标为0的值,如是有{1}则表示取数组下标为1的值,如果资源文件中有多个占位符,则:new String[]{name,参数2,参数3} String lmsg = getText("loginScuess",new String[]{name}); System.out.println(lmsg); } } |
避免页面与资源文件的耦合
用ActionContext将国际化的值设置到Request中,可以避免在页面使用<s:text>,从而在页面避免与资源文件的key的耦合性
Action中:
public class LoginAction extends ActionSupport { private String name; private String pd; public String login() { String lmsg = getText("loginScuess",new String[]{name}); //将资源文件中的国际化取出放进Request中: ActionContext.getContext().put("loginScuessAC", lmsg); } } |
页面使用:
<body> 登录成功,欢迎:<s:property value="name"/> <br/> action中取得国际化资源再在actionContext中设置的: ${requestScope.loginScuessAC } </body> |
包范围资源文件
对于一个大型软件而言,国际化文件是一项”浩大”的工程.模块多,资源文件内容多,维护是一一件很困难的事.为了更好的实现软件工程中的”分而治之”的原则,struts2允许针对不同的模板.不同的Action进行分组织国际化文件
在包的根路径下建立”package_language_country.properties”资源文件.一旦建立了国际化资源文件,在这个资源文件所在的包下的所有的action都可以访问资源文件.包范围资源文件不需要在struts.xml中配置.
例:
比如需要给user模块的action配置资源文件,那么只需要在所有的action所在的包下配置一个包范围配置文件即可.文件名叫:package_language_country.properties,其中_language_country可以省略.
配置文件:
package_zh_CN.properties
|
资源文件内容: loginScuess = 包范围资源文件,用户action下的资源文件,没有用占位符
|
配置struts.xml(包范围资源文件是不需要配置的,所以这里仍然是全局资源文件的配置,但是因为配置了包范围的资源文件,所以优先使用包资源文件)
<!-- 国际化资源文件 --> <constant name="struts.custom.i18n.resources" value="messageResource" /> <!-- 登录 --> <package name="login" extends="default" namespace="/login"> <action name="login" class="com.ts.action.LoginAction" method="login"> <result name="success">/wel.jsp</result> <result name="fail">/500.jsp</result> </action> </package> |
Action:
public class LoginAction extends ActionSupport { private String name; private String pd; public String login() { String lmsg = getText("loginScuess"); System.out.println(lmsg); ActionContext.getContext().put("loginScuessAC", lmsg); } //省略未写出 name和pd的get和set方法 } |
页面:
<body> <form action="login/login.action" method="post"> 登录名:<input name="name" type="text" /><br> 密码:<input name="pd" type="password" /><br> <input type="submit" value="登录" /> </form> </body> |
<body> 登录成功,欢迎:<s:property value="name"/> <br/> 国际化资源文件中取值: <br/> <s:text name="loginScuess"> <s:param><s:property value="name"/></s:param> </s:text><br/> action中取得国际化资源再在actionContext中设置的: ${requestScope.loginScuessAC } <br/> </body> |
效果:action中System.out.println()
|
效果:页面
|
Action范围资源文件
Struts2还允许为action单独指定国际化资源文件。在action类文件所在的路径建立名为:Actionname_language_country.properties文件。一旦建立这个系列的国际化资源文件,那么每个action都访问同action名的资源文件。
例:
为LoginAction.java指定一份单独的国际化资源文件:
LoginAction.properties资源文件:
|
内容: loginScuess = action范围的资源文件,没有占位符.
|
Struts.xml:一样不需要配置(同包范围资源文件的这一步)
Action: (同包范围资源文件的这一步)
页面:(同包范围资源文件的这一步)
效果:action中System.out.println()
|
页面效果:
|
Struts2注解(Annotation)
Struts2开始加入了“零配置”特性。通过使用零配置,struts2框架可以无须编写struts.xm、struts.properties配置文件,只需定义action处理类即可。
从Struts2.1版本起,Struts2官方就推荐使用Convention Plugin替换Codebehind Plugin来实现零配置。相对Codebehind Plugin而言,Convention Plugin有如下一些特点:
1. 通过包的命名习惯来指定Action的位置
2. 通过命名习惯来约定Result(包括Jsp、FreeMarker等)的路径
3. 类名对应于URL的约定转换
4. 包名对应于命名空间的约定转换
5. 遵循SEO规范的链接地址,比如:使用test-action来替换TestAction
6. 基于注解的Action名称约定
7. 基于注解的拦截器约定
8. 基于注解的命名空间约定
9. 基于注解的XWork包
10. 默认action以及默认的Result
等等。总之,使用Convention Plugin来实现零配置,会更灵活、更彻底,基本上可以做到不需要struts.xml来进行配置,甚至不需要注解进行配置,完全由Struts2依靠约定进行自动配置即可。
官方文档:struts-2.3.24.1\docs\docs\guides.html -> Convention Plugin (2.1.3+) ->Code behind hello world
如果使用struts2零配置,需要导入convention-plugin插件包:struts2-convention-plugin-2.3.24.1.jar。
struts2中Action与Servlet容器的耦合。主要有三种方式:
a.实现ServletRequestAware或ServletResponseAware接口。并提供对request或者response熟悉
的设置方法。一定要重写setServletRequest方法。
b.使用ActionContext(但不能获得response对象)。改方法方便单元测试。
c.使用ServletActionContext。ServletActionContext是ActionContext的子类。
首选使用ActionContext,其次ServletActionContext。
整合多个struts配置文件,在struts2的配置文件中使用include元素包含其他的配置文件。用于多模块开发