1. Struts2 框架中使用 Servlet 的 API 来操作数据
1.1 完全解耦合的方式
- Struts2 框架中提供了一个 ActionContext 类,该类中提供了一些方法:
-
static ActionContext getContext()
: 获取 ActionContext 对象实例; -
Map<Object> getParameters()
: 获取请求参数, 相当于request.getParameterMap()
; -
Map<Object> getSession()
: 获取代表 session 域的 Map 集合, 就相当于操作 session 域; -
Map<Object> getApplication()
: 获取代表 application 域的 Map 集合; -
void put(String key, Object value)
: 向 request 域中存入值;
-
1.2 使用原生 Servlet 的API方式
- Struts2 框架提供了一个 ServletActionContext 类,该类中提供了一些静态的方法:
static getPageContext()
static getRequest()
static getResponse()
static getServletContext()
// 需求: 使用 Struts2 作为 Web 层完成客户的新增功能
// regist.jsp
<h1>注册</h1>
<form action="{pageContext.request.contextPath}/registAction" method="post">
用户名: <input type="text" name="username"/><br/>
密 码: <input type="password" name="password"/><br/>
<input type="submit" value="注册"/>
</form>
// 完全解耦合的方式,操作数据 com.opensymphony.xwork2.ActionContext 包
ActionContext context = ActionContext.getContext();
// 获取到请求参数
Map<String,Object> map = context.getParameters();
// 遍历map, 获取数据
Set<String> keys = map.keySet();
for(String key : keys){
// Object 是数组
String[] val = (String[])map.get(key);
System.out.println(key+":"+Arrays.toString(val));
}
// 向 request 域中存入数据
context.put("msg","request域中数据");
// 向 session 域中存入数据
context.getSession().put("msg","session域中数据");
// 向 application 域中存入数据
context.getApplication().put("msg","application域中数据");
// 使用原生 Servlet 的 API 方式 org.apache.struts2.ServletActionContext 包
// 获取 request 对象
HttpServletRequest request = ServletActionContext.getRequest();
2. 结果页面的跳转
2.1 结果页面存在两种方式
1. 全局结果页面
- 如果
<package>
包中的一些action都返回 success,并且返回的页面都是同一个JSP页面,这样就可以配置全局的结果页面; - 全局结果页面针对当前包中的所有 Action, 如果局部还有结果页面,会优先局部的.
<package name="demo" extends="struts-default" namespace="/">
// 添加图书和删除图书成功后,都会跳转到 suc.jsp 页面
<global-results>
<result>/demo2/suc.jsp</result>
</global-results>
// 添加图书
<action name="addBook" class="cn.itcast.demo2.BookAction" method="add"/>
// 删除图书
<action name="deleteBook" class="cn.itcast.demo2.BookAction" method="delete"/>
</package>
2. 局部结果页面
<result>/demo3/suc.jsp</result>
2.2 结果页面的类型
-
<result>
标签包含两个属性:-
name
: 逻辑视图的名称; -
type
: 跳转的类型; 常见的结果类型,可以从struts-default.xml
中查找;-
dispatcher
: 转发, 默认值; -
redirect
: 重定向; -
chain
: 多个 action 之间跳转, 从一个 Action 转发到另一个 Action; -
redirectAction
: 多个 action 之间跳转,从一个 Action 重定向到另一个 Action; -
stream
: 文件下载时,使用;
-
-
// 重定向到 Action
// Action 类的编写
public class Demo3Action extends ActionSupport{
public String save(){
System.out.println("save....");
return SUCCESS;
}
public String update(){
System.out.println("update...");
return NONE;
}
}
// struts.xml
<action name="demo3Action_*" class="com.itheima.demo.Demo3Action" method="{1}">
// 注意路径的编写方式
<result name="success" type="redirectAction">demo3Action_update</result>
</action>
3. Struts 框架的数据封装
3.1 概述
- Struts2 提供了两类数据封装的方式
- 属性驱动;
- 模型驱动(使用较多);
3.2 属性驱动
3.2.1 提供对应属性的 set 方法进行数据的封装(使用较多)
- 表单的哪些属性需要封装数据,那么在对应的 Action 类中提供该属性的 set 方法即可;
- 表单中提交的数据,最终调用 Action 类中的 setXxx 方法,赋值给全局变量;
- 注意点:
- Struts2 的框架采用拦截器完成数据的封装;
- 如果属性特别多,需要提供特别多的 set 方法,而且还需要手动将数据存入到对象中;
- 这种情况下, Action 类就相当于一个 JavaBean. Action类既封装数据,又接收请求数据,耦合性较高,
没有体现出 MVC 思想;
3.2.2 在页面上,使用 OGNL 表达式进行数据封装
- 在页面中使用OGNL表达式进行数据的封装,就可以直接把属性封装到某一个 JavaBean 的对象中;
- 在页面中定义一个 JavaBean, 并且提供 set 方法;
- 表单的写法:
<input type="text" name="user.username"/>
- 注意:
- 只提供一个 set 方法还不够,还需要提供 user 属性的 get 方法;
- 先调用 get 方法,判断一下是否有 user 对象的实例对象;如果没有,就先创建 user 对象,并封装一个数据,
然后调用set方法把拦截器创建的对象注入进来; 再调用 getUser 来封装数据;
3.3 模型驱动
- 手动实例化 JavaBean, 即:
private User user = new User()
; - 必须实现 ModelDriven 接口,实现
getModel()
的方法, 在getModel()
方法中返回 user 即可!
// 属性驱动
// regist.jsp
<form action="{pageContext.request.contextPath}/regist.action" method="post">
用户名: <input type="text" name="username"/><br/>
密 码: <input type="password" name="password"/><br/>
<input type="submit" value="注册"/>
</form>
// 属性驱动方式一, Action 类中提供对应属性的 set 方法
public class RegistAction extends ActionSupport{
// 定义属性
private String username;
private String password;
// 只需要提供对应属性的 set 方法
// 拦截器调用 setXxx 方法,将属性封装,赋值给全局变量 username,password
public void setUsername(String username){
this.username = username;
}
public void setPassword(String password){
this.password = password;
}
// regist 方法
public String regist(){
// 输出打印
System.out.println(username+"::"+password);
return NONE;
}
}
// 属性驱动方式二, OGNL 表达式
// regist.jsp, 页面中使用 OGNL 表达式
<form action="{pageContext.request.contextPath}/regist.action" method="post">
// 此处 user.username 与 Action 类中的属性 user 一致
用户名: <input type="text" name="user.username"/><br/>
密 码: <input type="password" name="user.password"/><br/>
<input type="submit" value="注册"/>
</form>
// 需要提供 JavaBean
// User.class
public class User{
private String username;
private String password;
// 提供 get 和 set 方法
public String getUsername(){
return username;
}
public void setUsername(String username){
this.username = username;
}
public String getPassword(){
return password;
}
public void setPassword(String password){
this.password = password;
}
public String toString(){
....
}
}
// Action 类
public class RegistAction extends ActionSupport{
// 定义属性
private User user;
// 提供 get 和 set 方法
public User getUser(){
return user;
}
public void setUser(User user){
this.user = user;
}
public String regist(){
System.out.println(user);
return NONE;
}
}
// 模型驱动
// regist.jsp
<form action="{pageContext.request.contextPath}/regist.action" method="post">
用户名: <input type="text" name="username"/><br/>
密 码: <input type="password" name="password"/><br/>
<input type="submit" value="注册"/>
</form>
// User.class
同上
// Action 类, 需要实现 ModelDriven 接口
public class RegistAction extends ActionSupport implements ModelDriven<User>{
// 手动实例化
private User user = new User();
// 实现 getModel() 方法
// 即获取模型对象
public User getModel(){
return user;
}
public String regist(){
System.out.println(user);
return NONE;
}
}
3.4 Struts2 把数据封装到集合中
- 把数据封装到 Collection 中
- 因为 Collection 接口都会有下标值,所有页面的写法会有一些区别,
<input type="text" name="products[0].name"/>
; - 在 Action 类中,需要提供 products 集合,并且提供 get 和 set 方法;
- 因为 Collection 接口都会有下标值,所有页面的写法会有一些区别,
- 把数据封装到 Map 中
- Map 集合是键值对的形式,页面的写法:
<input type="text" name="map['one'].name"/>
- Action 类中提供 map 集合,并且提供 get 和 set 方法;
- Map 集合是键值对的形式,页面的写法:
// 向 List 集合中封装数据 (默认情况下, 采用属性驱动的方式)
// regist.jsp
<form action="{pageContext.request.contextPath}/regist.action" method="post">
用户名1: <input type="text" name="list[0].username"/><br/>
密 码1: <input type="password" name="list[0].password"/><br/>
用户名2: <input type="text" name="list[1].username"/><br/>
密 码2: <input type="password" name="list[1].password"/><br/>
<input type="submit" value="注册"/>
</form>
// User.class
同上
// Action 类
public class RegistAction extends ActionSupport{
// 定义属性
private List<User> list;
// 提供 get 和 set 方法
public List<User> getList(){
return list;
}
public void setList(List<User> list){
this.list = list;
}
public String regist(){
for(User user : list){
System.out.println(user);
}
return NONE;
}
}
// 向 Map 集合中封装数据
// regist.jsp
<form action="{pageContext.request.contextPath}/regist.action" method="post">
用户名1: <input type="text" name="map['one'].username"/><br/>
密 码1: <input type="password" name="map['one'].password"/><br/>
用户名2: <input type="text" name="map['two'].username"/><br/>
密 码2: <input type="password" name="map['two'].password"/><br/>
<input type="submit" value="注册"/>
</form>
// User.class
同上
// Action 类
public class RegistAction extends ActionSupport{
// 定义属性
private Map<String,User> map;
// 提供 get 和 set 方法
public Map<String,User> getMap(){
return map;
}
public void setMap(Map<String,User> map){
this.map = map;
}
public String regist(){
System.out.println(map);
return NONE;
}
}
4. 拦截器技术
4.1 拦截器的概述
- 拦截器就是 AOP(Aspect-Oriented Programming)的一种实现. AOP 是指用于在某个方法或字段被访问之前,
进行拦截,然后在之前或之后加入某些操作; - 拦截器采用"责任链"模式
- 在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链;
- 责任链中每一个节点,都可以继续调用下一个节点,也可以组织流程继续执行;
- Struts2 中可以定义很多个拦截器,将多个拦截器按照特定顺序组成拦截器栈.
4.2 拦截器和过滤器的区别
- 拦截器是基于 java 反射机制的,而过滤器是基于函数回调的;
- 过滤器依赖于 Servlet 容器, 而拦截器不依赖于 Servlet 容器;
- 拦截器只能对 Action 请求起作用(即Action 中的方法),
而过滤器是用于过滤从客户端发送到服务端请求的,其中包括CSS, JSP, JS等;
4.3 自定义拦截器和配置
4.3.1 自定义拦截器
- 编写拦截器需要实现 interceptor 接口,实现接口中的三个方法;
- 也可以继承 interceptor 接口的已知实现类 AbstractInterceptor;
- 或者继承 interceptor 接口的已知实现类 MethodFilterInterceptor, 可以对指定方法放行;
// 编写拦截器
public String DemoInterceptor extends AbstractInterceptor {
// intercept 方法: 在 action 执行之前,拦截
public String intercept(ActionInvocation invocation) throws Exception{
System.out.println("拦截器执行之前.....");
// 执行下一个拦截器
String result = invocation.invoke();
System.out.println("拦截器执行之后....");
return result;
}
}
4.3.2 在 struts.xml 中配置拦截器
// 第一种方式:
// 在 <package> 包中定义拦截器,出现在 <package> 包的上方
<interceptors>
<interceptor name="loginInterceptor" class="cn.itcast.interceptor.LoginInterceptor"/>
</interceptors>
// 在某个 action 中引入拦截器
<interceptor-ref name="loginInterceptor"/>
// 注意:
// 如果引入了自定义的拦截器,Struts2 框架默认的拦截器就不会再执行了,所以需要引入 Struts2 默认的拦截器
<interceptor-ref name="defaultStack"/>
// 第二种方式
// 在 <package> 包中定义拦截器的时候,自己直接定义一个拦截器栈
<interceptors>
<interceptor name="loginInterceptor" class="cn.itcast.interceptor.LoginInterceptor"/>
<interceptor-stack name="myStack">
<interceptor-ref name="loginInterceptor"/>
<interceptor-ref name="defaultStack"/>
</interceptor-stack>
</interceptors>
// 在 action 中引入自定义拦截器栈
<action name="book_*" class="cn.itcast.action.BookAction" method="{1}">
<interceptor-ref name="myStack"/>
</action>
参考资料