Struts2学习总结
前言
以下内容为观看:传智播客黑马程序员Struts2视频 总结!
一、认识struts2
1.1 Struts2的前世今生
在介绍struts2之前,我们先来认识以下struts1。struts1是最早基于MVC模式的轻量级Web框,它的目的是为了减少在运用MVC设计模型来开发Web应用的时间。随着技术的进步,struts1的局限性也暴露出来,于是struts2应运而生。struts2是在struts1与WebWork技术的基础上进行合并后的全新框架。接下来,让我们认识以下struts2。
1.2 什么是Struts2
Struts2 是目前较为普及和成熟的基于MVC设计模式的web应用程序框架,它不仅仅是Struts1 的升级版本,更是一个全新的Struts架构。最初,是以WebWork框架和Struts框架为基础,通过提供增强和改进的Struts框架,进而实现简化web技术人员开发工作的目标。不久之后,Webwork框架和Struts社区联合创造了现在流行的Struts2框架。
struct2的MVC架构:模型(Model)-视图(View)-控制器(Controller),通常简称MVC,是一种开发web应用程序的软件设计模式。该软件设计模式由以下三部分组成:
- 模型——属于软件设计模式的底层基础,主要负责数据维护。
- 视图——这部分是负责向用户呈现全部或部分数据。
- 控制器——通过软件代码控制模型和视图之间的交互。
MVC普及的原因在于它区分了应用程序的逻辑层和用户界面层,并支持开发关注点的分离。在MVC模式下,控制器接收了所有来自应用程序的请求后,调用模型去准备视图所需要的数据,然后视图使用由控制器提供的数据最终生成一个可视的响应。
二、struts2入门
2.1 创建web工程并引入jar包
首先创建一个web工程(省略),然后引入相应的Jar包。
在下载好的Struts2的根目录下的lib文件夹内,存在很多的Jar包,但是在开发中,一般是用不到全部的Jar包,所以我们可以参考struts2给的一些实例代码, 引入相应的jar包。
打开apps文件夹,我们发现有很多struts2给的项目实例:
我们解压struts2-blank.war包,可以得到一个struts2的空项目,然后复制其lib目录下的所有jar包即可。
2.2 编写Action以及页面
2.2.1 创建页面
1.编写链接页面:index.jsp
<body>
<a href="${pageContext.request.contextPath }/struts2Action.action">点我跳转</a>
</body>
2.编写跳转成功页面:success.jsp
<body>
<h1>struts2 Success!</h1>
</body>
2.2.3 编写Action:FirstStruts2 类
public class FirstStruts2 {
public String execute() {
System.out.println("HelloAction已经执行了!!!");
return "success";
}
}
2.3 配置Action:
在src下创建struts2.xml文件,并编写以下内容。配置文件内容属性,将会在第三部分进行介绍。
<?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>
<package name="demo01" extends="struts-default" namespace="/">
<action name="struts2Action" class="com.itheima.demo01.FirstStructs2">
<result name="success" >/success.jsp</result>
</action>
</package>
</struts>
2.4 配置核心过滤器
在web.xml中进行配置。
<filter>
<filter-name>structs2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>structs2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
2.5 测试
当点击首页的 点我跳转 链接时,将会跳转到success.jsp页面。
2.6 struts2执行流程
- 当用户点击链接,客户端发送请求到服务器。
- 首先经过服务器的前端控制器(即在web.xml中配置的核心过滤器),过滤器执行一组拦截器,完成部分的功能。
- 拦截器执行完之后,执行目标Action
- Action返回一个结果视图,根据result的配置进行页面跳转。
页面执行流程图:
三、struts2配置
3.1 Action配置
struts2框架的核心配置文件就是struts2.xml文件,该文件主要用来配置Action和请求的对应关系。接下来看一下它内部的相关配置。
3.1.1 package相关配置
struts2中的每个包就是多个Action与多个拦截器引用的集合。相关属性如下:
- name:指定package的名称,必填属性,该名称是该包被其他包引用的key。
- extends: 可选属性,指定该包继承自其他包。
- namespace: 可选属性,定义该包的命名空间,该命名空间与Action的名称共同决定了访问路径。
- abstract: 可选属性,指定该包是否是一个抽象包。
3.1.2 Action的相关配置
Action映射是框架中必不可少的工作单元。它的作用就是将一个请求的URL映射到一个Action类。相关属性如下:
- name:必填属性,配置Action的名称。
- class:可选属性,指定Action所对应的Java类。
- method:可选属性,指定该Java类中所要调用的方法。
- converter:可选属性,指定类型转换器的类。
3.1.2.1 result相关配置
result是配置Action执行成功后,要跳转的页面,相关属性如下:
- name:可选属性,指定result的名称,默认为SUCCESS。
- type:指定跳转的方式,有如下几种跳转方式:
- dispatcher(服务端页面跳转):默认值,请求转发
- redirect(客户端页面跳转):重定向(Action–JSP)
- chain(动作链跳转):转发
- redirectAction(客户端Action跳转):重定向(Action–Action)
3.2 常量的配置
在struts2中定义了很多常量,这些常量在/org/apache/struts2/default.properties中进行定义配置,当我们要修改这些常量的时候,不能直接在这个文件中进行修改,但是有如下三种方式进行修改。
- 在struts.xml中使用元素配置常量。
- 在strus.properties文件中配置常量。
- 在web.xml文件中使用元素配置常量。
接下来对这三种方式分别进行讲解。
3.2.1 在struts.xml中配置常量
在struts.xml文件中进行配置是最常用的一种方式,它有两个属性:
- name:指定常量的常量名
- value:指定常量的常量值
<constant name="struts.i18n.encoding" value="UTF-8"></constant>
3.2.2 在strus.properties文件中配置常量
struts.properties是一个标准的properties文件,其格式是key-value对,所以其配置方式与default.properties相同。
struts.i18n.encoding=UTF-8
3.2.3 在web.xml文件中配置常量
在web.xml中配置核心过滤器时,通过初始化其参数,来配置常量。
<filter>
<filter-name>structs2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
<init-param>
<param-name>struts.i18n.encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
3.3 分模块开发的配置
在实际开发中,如果很多人同时使用struts.xml文件配置,就会很麻烦,而且让项目非常不稳定,所以struts2提供了标签来解决这么问题。它的用法很简单,就是新建一个xml文件,然后通过include进行引入就可以了。
<?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>
<include file="com/itheima/demo1/struts-demo1.xml"></include>
<include file="com/itheima/demo2/struts-demo2.xml"></include>
</struts>
3.4 配置文件的加载顺序
每次客户端请求都必须经过核心过滤器,该过滤器有两个功能:预处理和执行,而预处理就是用来加载配置文件的,通过查看其源码我们可以得到配置文件的加载顺序:
//记载常量:default.properties
init_DefaultProperties(); // [1]
//加载struts-default.xml、struts-plugin.xml、struts.xml
init_TraditionalXmlConfigurations(); // [2]
//加载struts.properties
init_LegacyStrutsProperties(); // [3]
//加载配置提供类
init_CustomConfigurationProviders(); // [5]
// 加载web.xml中过滤器初始化参数
init_FilterInitParameters() ; // [6]
//加载Bean对象
init_AliasStandardObjects() ; // [7]
所以通过源代码我们可以得到strus2的配置文件的加载顺序:
- default.properties
- struts-default.xml
- struts-plugin.xml
- struts.xml
- struts.properties
- web.xml
注意:后加载的配置文件中配置的常量会覆盖前边加载的文件配置的常量!
四、Action类的编写
在Struts中Action作为框架的核心类,实现用户的请求处理,它的实现一共有三种方式:
4.1 Action是一个POJO类
在Struts2中Action可以直接是一个POJO类,在该类中要有一个无参的公共构造方法,我们的入门例子就是使用的这种方式。在这个类中还要有一个execute()方法,定义的格式如下:
public class FirstStructs2 {
public String execute() {
System.out.println("HelloAction已经执行了!!!");
return "success";
}
}
对execute方法的要求如下:
- 方法的权限修饰符为public。
- 该方法要返回一个字符串,该字符串与result的那么对应。
- 方法没有参数。
4.2 Action类实现Action接口
为了让开发更加的规范,Struts2提供了一个Action接口,该接口定义了处理类实现的规范。
public class FirstStruts2 implements Action {
@Override
public String execute() throws Exception {
return null;
}
}
该接口还定义了5个常量:
public static final String SUCCESS = “success”; 成功执行
public static final String NONE = “none”; 页面不跳转
public static final String ERROR = “error”; 跳转到错误的页面
public static final String INPUT = “input”; 数据检验的时候跳转的路径
public static final String LOGIN = “login”; 跳转到登陆页面
4.3 Action类继承ActionSupport类
ActionSupport实现Action接口,是Struct2中Action接口的默认实现类。
public class FirstStruts2 extends ActionSupport {
@Override
public String execute() throws Exception {
return super.execute();
}
}
4.4 Action类的访问
在struts.xml中配置的Action一个Action只能对应一个方法,那么如果要让一个Action处理多个操作怎么办呢?
4.4.1 通过配置method
在struts.xml中配置action的method属性可以实现,但是这样会使得配置文件非常的复杂。
<package name="demo01" extends="struts-default" namespace="/">
<action name="struts2Action_save" class="com.itheima.demo01.FirstStructs2" method="save">
<result name="success" >/success.jsp</result>
</action>
<action name="struts2Action_find" class="com.itheima.demo01.FirstStructs2" method="find">
<result name="success" >/success.jsp</result>
</action>
<action name="struts2Action_delete" class="com.itheima.demo01.FirstStructs2" method="delete">
<result name="success" >/success.jsp</result>
</action>
<action name="struts2Action_update" class="com.itheima.demo01.FirstStructs2" method="update">
<result name="success" >/success.jsp</result>
</action>
</package>
4.4.2 使用通配符
<package name="demo01" extends="struts-default" namespace="/">
<action name="struts2Action_*" class="com.itheima.demo01.FirstStructs2" method="{1}">
<result name="success" >/success.jsp</result>
</action>
</package>
4.5 原生servlet api的访问
在struts2中,Action没有直接和Servlet API进行耦合,也就是说在Action中不能直接访问Servlet API,那当我们需要操作这些对象的时候应该怎么办呢?
4.5.1 方式一:通过ActionContext类访问
我们知道,在Servlet的底层,对于request、session、application这些域对象都是使用Map来实现的,而ActionContext中保存了Action执行的所有对象,当然也包含了request、response这些对象,通过对对其操作,可以实现对Servlet API的间接访问,为什么说是间接访问呢?因为是直接操作的其Map对象,而不是api本身。
@Override
public String execute() throws Exception {
ActionContext context = ActionContext.getContext();
Map<String,Object> map = context.getParameters();
for(String s : map.keySet()) {
String[] str = (String[]) map.get(s);
System.out.println(s+" "+Arrays.toString(str));
}
context.put("reqName", "我说req<br>");
context.getApplication().put("appName", "我是APP<br>");
context.getSession().put("sessionName","我是Sess<br>");
return super.execute();
}
常用方法:
1、public Object get(Object key):取得HttpServletRequest中key的值;
2、void put(String key,Object value):设置HttpServletRequest中key的值为value;
3、public Map getApplication():获取封装了ServletContext的Map对象;
4、void setApplication(Map application):设置ServletContext实例;
5、static ActionContext getContext():静态方法,获取系统的ActionContext实例;
6、Map getParameters():类似于HttpServletRequest中的getParametersMap方法;
7、public Map getSession():获取封装了HttpSession的Map对象;
8、void setSession(Map session):直接传入一个Map实例,将该Map实例里的key-value
4.5.2 方式二:通过ServletActionContext访问
为了直接访问Servlet API,Struts2提供了ServletActionContext类,该类包含几个静态方法,通过这几个方法,可以直接操作Servlet API。方法如下:
- static HttpServletResquest getRequest(); 获取Web应用HttpServletResquest对象
- static HttpServletResponse getResponse(); 获取Web应用HttpServletResponse对象
- static ServletContext getServletContext(); 获取Web应用ServletContext对象
- static PageContext getPageContext(); 获取Web应用pageContext对象
public String demo02() throws Exception {
HttpServletRequest request = ServletActionContext.getRequest();
Map<String, String[]> parameterMap = request.getParameterMap();
for (String key : parameterMap.keySet()) {
System.out.println(key+" "+Arrays.toString(parameterMap.get(key)));
}
request.setAttribute("reqName", "第二种方法之request<br>");
ServletActionContext.getServletContext().setAttribute("appName", "我是第二种方法之appliacation<br>");
request.getSession().setAttribute("sessionName", "我是第二种方法之Session<br>");
return SUCCESS;
}
4.5.3 通过特定接口访问
struts2还提供了一系列接口,通过实现这些接口,可以实现对Servlet API的访问。
- ServletRequestAware :实现该接口的Action可以直接访问web应用的HttpServletRequest
- ServletResponseAware :实现该接口的Action可以直接访问web应用的HttpServletResponse
- SessionAware :实现该接口的Action可以直接访问web应用的TttpSession
- ServletContextAware :实现该接口的Action可以直接访问web应用的ServletContex实例
public class Demo5Action extends ActionSupport implements ServletRequestAware {
private HttpServletRequest request;
@Override
public String execute() throws Exception {
System.out.println("原生request:"+request);
return SUCCESS;
}
@Override
public void setServletRequest(HttpServletRequest request) {
this.request=request;
}
}
4.6 数据封装
在实际开发中,有可能遇到批量向数据库中插入记录,需要在页面中将数据封装到集合中,这就需要对数据进行封装,struts2提供了对数据封装的功能。
4.6.1 属性驱动
4.6.1.1 方式一:提供属性的set方法
在struts2中,可以直接在Action中定义各种Java基本类型的字段,使这些字段与表单中的数据相对应,这样struts2可以利用这些字段进行数据传递。传递举例:
- jsp页面
<body>
<h1>用户信息页面</h1>
<h3>方式一:属性</h3>
<form action="${pageContext.request.contextPath }/datademo.action" method="post">
姓名:<input type="text" name="username"/><br>
密碼:<input type="password" name="password"/><br>
年龄:<input type="text" name="age"/><br>
日期:<input type="text" name="date"/><br>
薪资:<input type="text" name="salar"/><br>
<input type="submit" value="提交"/>
</form>
</body>
- Action
public class DataDemo extends ActionSupport{
private String username;
private String password;
private Integer age;
private Date date;
private Double salar;
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
public void setAge(Integer age) {
this.age = age;
}
public void setDate(Date date) {
this.date = date;
}
public void setSalar(Double salar) {
this.salar = salar;
}
@Override
public String execute() throws Exception {
System.out.println(username);
System.out.println(password);
System.out.println(age);
System.out.println(date);
System.out.println(salar);
return NONE;
}
}
4.6.1.2 方式二:使用OGNL表达式
也可以在jsp页面中使用OGNL表达式,然后再Action中定义一个实现类,以及get/set方法,可以实现同样的功能。举例
- JSP页面
<body>
<h1>用户信息页面</h1>
<h3>方式二:OGNL</h3>
<form action="${pageContext.request.contextPath }/datademo.action" method="post">
姓名:<input type="text" name="user.username"/><br>
密碼:<input type="password" name="user.password"/><br>
年龄:<input type="text" name="user.age"/><br>
日期:<input type="text" name="user.date"/><br>
薪资:<input type="text" name="user.salar"/><br>
<input type="submit" value="提交"/>
</form>
</body>
- Action
public class DataDemo extends ActionSupport{
private User user;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
@Override
public String execute() throws Exception {
System.out.println(user.getUsername());
System.out.println(user.getPassword());
System.out.println(user.getAge());
System.out.println(user.getDate());
System.out.println(user.getSalar());
return NONE;
}
}
4.6.2 模型驱动
Action处理请求参数的第二种方式,让Action类实现ModelDivern接口来接收请求参数,Action类必须实现MOdelDriven接口,然后重写getModel()方法即可。传递举例:
- jsp 页面
<body>
<h1>用户信息页面</h1>
<h3>方式三:模型驱动</h3>
<form action="${pageContext.request.contextPath }/datademo.action" method="post">
姓名:<input type="text" name="username"/><br>
密碼:<input type="password" name="password"/><br>
年龄:<input type="text" name="age"/><br>
日期:<input type="text" name="date"/><br>
薪资:<input type="text" name="salar"/><br>
<input type="submit" value="提交"/>
</form>
</body>
- Action
public class DataDemo extends ActionSupport implements ModelDriven<User>{
private User user = new User();
@Override
public User getModel() {
return user;
}
@Override
public String execute() throws Exception {
System.out.println(user.getUsername());
System.out.println(user.getPassword());
System.out.println(user.getAge());
System.out.println(user.getDate());
System.out.println(user.getSalar());
return NONE;
}
}
4.6.3 复杂类型数据的封装
在实际的开发中,我们可能需要批量插入用户或者批量插入其他的对象,所以需要封装到集合或者Map中,接下来就以这两种类型的数据进行演示。
-
封装到List集合中
- 编写页面
<form action="/productSave.action" method="post"> 名称:<input type="text" name="list[0].name"><br> 价格:<input type="text" name="list[0].prict"><br> 名称:<input type="text" name="list[1].name"><br> 价格:<input type="text" name="list[1].prict"><br> 名称:<input type="text" name="list[2].name"><br> 价格:<input type="text" name="list[2].prict"><br> <input type="submit"> </form>
- 编写Action
public class ProductAction extends ActionSupport { private List<Product> list; //必须提供get,set方法 public List<Product> getList() { return list; } public void setList(List<Product> list) { this.list = list; } public String listTest(){ for (Product product : list) { System.out.println("list:"+product); } return NONE; } }
-
封装到Map中
- 编写页面
<form action="/productMap.action" method="post"> 名称:<input type="text" name="map['o'].name"><br> 价格:<input type="text" name="map['o'].price"><br> 名称:<input type="text" name="map['t'].name"><br> 价格:<input type="text" name="map['t'].price"><br> 名称:<input type="text" name="map['th'].name"><br> 价格:<input type="text" name="map['th'].price"><br> <input type="submit"> </form>
- 编写Action
public class ProductAction2 extends ActionSupport { private Map<String,Product> map; public Map<String, Product> getMap() { return map; } public void setMap(Map<String, Product> map) { this.map = map; } public String mapTest(){ for (String key : map.keySet()) { Product product=map.get(key); System.out.println(key+" "+product); } return NONE; } }
五、OGNL与ValueStack
5.1 OGNL
5.1.1 什么是OGNL?
OGNL是Object-Graph Navigation Language的缩写,全称为对象图导航语言,是一种功能强大的表达式语言,它通过简单一致的语法,可以任意存取对象的属性或者调用对象的方法,能够遍历整个对象的结构图,实现对象属性类型的转换等功能。
5.1.2 OGNL特点
- 支持对象方法调用。如 objName.methodName()。
- 支持类静态方法调用和值访问,表达式的格式为 @[类全名(包括包路径)]@[方法名|值名]。如 @java.lang.Math@random()。
- 支持赋值操作和表达式串联。如 price=100,discount=0.8,在方法 calculatePrice() 中进行乘法计算会返回 80。
- 访问 OGNL 上下文(OGNL context)和 ActionContext。
- 操作集合对象。
5.1.3 OGNL入门
OGNL 的操作实际上是围绕 OGNL 结构的三个要素进行的,它也是由这三要素组成的,分别是表达式(expression)、上下文对象(context)和根对象(root)。
-
表达式:表达式是整个 OGNL 的核心,在ognl中想要执行取值,赋值,调用方法等等操作,你都需要用表达式表示。通过表达式,底层会解析出来你的想要操作。
-
根对象:可以理解为OGNL的操作对象,表达式规定做什么,而该对象就指定对谁操作。
-
上下文对象:上下文对象规定了 OGNL 操作“在哪里进行”。context 对象是一个 Map 类型的对象,在表达式中访问 context 中的对象,需要使用 # 号加对象名称,即“# 对象名称”的形式。
@Test
public void demo01() throws OgnlException {
OgnlContext context = new OgnlContext();
Object root = context.getRoot();
//访问静态方法
Object value = Ognl.getValue("@java.lang.Math@random()", context, root);
System.out.println(value);
//调用对象的方法
Object value2 = Ognl.getValue("'Struts2Ognl'.length()",context,root);
System.out.println(value2);
//获取root的数据
User user = new User();
user.setUsername("张三");
context.setRoot(user);
String value3;
try {
value3 = (String) Ognl.getValue("username", context, context.getRoot());
System.out.println(value3);
} catch (OgnlException e) {
e.printStackTrace();
}
//获取context的数据
context.put("name","lisi");
String value4;
try {
value4 = (String) Ognl.getValue("#name", context, root);
System.out.println(value4);
} catch (OgnlException e) {
e.printStackTrace();
}
}
5.1.4 OGNL在Struts2环境下的使用
-
引入标签库
<%@ taglib uri="/struts-tags" prefix="s" %>
-
在页面中使用
<body> <h1>数据显示页面</h1> <!-- 访问对象方法 --> <s:property value="'Struts2Ognl'.length()"/><br> <!-- 访问静态方法 --> <s:property value="@java.lang.Math@random()"/><br> <s:debug></s:debug> </body>
5.1.5 OGNL中的特殊符号
-
#符号的用法
- 访问非根对象的属性。如访问 OGNL 上下文和 Action 上下文。由于 Struts2 中值栈被视为根对象,所以访问其他非根对象时,需要加
#
前缀。#相当于 ActionContext.getContext()。例如‘#session.user’表达式相当于 ActionContext.getContext().getSession().getAttribute(“user”),‘#request.userName’表达式相当于 request.getAttribute(“userName”); - 用于过滤和投影集合。如 books.{?#this.price>25}。
- 构造 Map。如:
<s:radio name="sex" list="{'男','女'}"></s:radio><s:radio name="sex" list="#{'0':'男','1':'女'}"></s:radio>
- 访问非根对象的属性。如访问 OGNL 上下文和 Action 上下文。由于 Struts2 中值栈被视为根对象,所以访问其他非根对象时,需要加
-
%符号的用法
- 在标签的属性值被理解为字符串类型时,告诉执行环境‘%{}’中的是 OGNL 表达式,并计算 OGNL 表达式的值。
<s:property value="%{'#request.msg'}"/>
-
$符号的用法
- 在配置文件中可以使用OGNL表达式。
<action name="download1" class="cn.itcast.demo2.DownloadAction"> <result name="success" type="stream"> <param name="contentType">${contentType}</param> <param name="contentDisposition">attachment;filename=${downFilename}</param> </result> </action>
5.2 ValueStack
5.2.1 什么是值栈?
Struts2将XWork对Ognl的扩展这一套机制封装起来,这个对象叫ValueStack。ValueStack实际上就是一个容器。它由Struts框架创建,当前端页面如jsp发送一个请求时,Struts的默认拦截器会将请求中的数据进行封装,并入ValueStack的栈顶。
简单来说,值栈(ValueStack)就是Struts2中提供的一种类似域对象的工具,客户端发起一个请求的时候,创建一个action实例的同时,创建一个值栈实例,这个实例贯穿整个action的生命周期,Struts用OGNL将请求action的参数封装为对象,储存到值栈中,通过表达式,取其中的值
5.2.2 值栈的内部结构
值栈的结构可以分为两个部分,第一个部分为root,结构是list集合,第二部分是context,结构是Map集合。root中存放的是操作的对象,而context中存放的是映射关系。当我们在jsp页面中使用<s:debug></s:debug>
的时候,就可以在页面中看到值栈的结构。我们之所以可以使用ActionContext获取session等的Map集合,就是因为在值栈中存有关系映射。
5.2.3 如何获取值栈?
- 获取值栈有两种方法
@Override
public String execute() throws Exception {
//The First method.
ValueStack valueStack1 = ActionContext.getContext().getValueStack();
//The Second method.
ValueStack valueStack2 = (ValueStack) ServletActionContext.getRequest().getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);
System.out.println(valueStack1==valueStack2);
return super.execute();
}
- 操作值栈
public String setValueStack() {
ValueStack valueStack = ActionContext.getContext().getValueStack();
user = new User("张三","this zhangsan");
valueStack.push(user);
valueStack.set("name", "Mr.BoBo");
return SUCCESS;
}
六、拦截器
6.1 什么是拦截器
struts2能完成数据的设置,数据的封装,数据的类型转换,数据的校验等等。struts2是如何来完成这些功能的?struts2的所有功能都是由拦截器来完成的。拦截器是struts2的核心。拦截器是一个类似于过滤器的类。在执行action的处理方法前会 先执行拦截器,然后再执行action的处理方法,然后再执行拦截器,再响应. struts2的所有功能都是由拦截器来实现的,而拦截器在struts2中时可以自由配置和自由装配的。所以struts2的所有功能也都是可插拔的。并且struts2中的拦截器是可以自定义的,所以如果struts2没有提供项目所需的功能时,可以通过自定义拦截器来实现。
6.2 拦截器的实现原理
在开始讲解Struts的时候已经说明过了struts-default.xml这个文件,它定义了Struts的所有拦截器。而了解拦截器的实现原理就要对struts2的执行流程做深入的了解。
6.2.1 再谈struts2执行流程
客户端向服务器发送一个Action的请求,执行核心过滤器(doFilter)方法。在这个方法中,调用executeAction()方法,在这个方法内部调用dispatcher.serviceAction();在这个方法内部创建一个Action代理,最终执行的是Action代理中的execute(),在代理中执行的execute方法中调用ActionInvocation的invoke方法。在这个方法内部递归执行一组拦截器(完成部分功能),如果没有下一个拦截器,就会执行目标Action,根据Action的返回的结果进行页面跳转。
6.3 自定义拦截器
我们学习了拦截器的定义,以及执行原理,那么如何编写一个拦截器呢?
如果要定义拦截器,就需要直接或者间接的实现intercept接口,然后进行配置。
- 编写拦截器类
public class InterceptStudy extends AbstractInterceptor {
@Override
public String intercept(ActionInvocation invocation) throws Exception {
System.out.println("InterceptStudy Start...");
String obj = invocation.invoke();
System.out.println("InterceptStudy Start...");
return obj;
}
}
-
配置拦截器
- 配置方式一:定义拦截器
<package name="demo2" extends="struts-default" namespace="/"> <interceptors> <interceptor name="intercepter01" class="com.itheima.demo2.InterceptStudy"> </interceptor> </interceptors> <action name="valueStackAction02" class="com.itheima.demo2.ValueStackAction02" method="setValueStack"> <result>/success.jsp</result> <!-- 一旦引入自定义拦截器,默认拦截器就不执行了,所以需要引入默认拦截器 --> <interceptor-ref name="defaultstack"></interceptor-ref> <interceptor-ref name="intercepter01"></interceptor-ref> </action> </package>
- 配置方式二:定义拦截器栈
<package name="demo2" extends="struts-default" namespace="/"> <interceptors> <interceptor name="intercepter01" class="com.itheima.demo2.InterceptStudy"></interceptor> <interceptor-stack name="mystack"> <!-- 一旦引入自定义拦截器,默认拦截器就不执行了,所以需要引入默认拦截器 --> <interceptor-ref name="defaultstack"></interceptor-ref> <interceptor-ref name="intercepter01"></interceptor-ref> </interceptor-stack> </interceptors> <action name="valueStackAction02" class="com.itheima.demo2.ValueStackAction02" method="setValueStack"> <result>/success.jsp</result> <interceptor-ref name="mystack"></interceptor-ref> </action> </package>
七、总结
以上内容即为对struts2知识的全部总结,如有错误,欢迎指正!