structs2学习?这一篇就够了!

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执行流程

  1. 当用户点击链接,客户端发送请求到服务器。
  2. 首先经过服务器的前端控制器(即在web.xml中配置的核心过滤器),过滤器执行一组拦截器,完成部分的功能。
  3. 拦截器执行完之后,执行目标Action
  4. 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的配置文件的加载顺序:

  1. default.properties
  2. struts-default.xml
  3. struts-plugin.xml
  4. struts.xml
  5. struts.properties
  6. 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。方法如下:

  1. static HttpServletResquest getRequest(); 获取Web应用HttpServletResquest对象
  2. static HttpServletResponse getResponse(); 获取Web应用HttpServletResponse对象
  3. static ServletContext getServletContext(); 获取Web应用ServletContext对象
  4. 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的访问。

  1. ServletRequestAware :实现该接口的Action可以直接访问web应用的HttpServletRequest
  2. ServletResponseAware :实现该接口的Action可以直接访问web应用的HttpServletResponse
  3. SessionAware :实现该接口的Action可以直接访问web应用的TttpSession
  4. 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 表达式,并计算 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知识的全部总结,如有错误,欢迎指正!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值