Struts笔记二:栈值的内存区域及标签和拦截器
值栈和ognl表达式
1.只要是一个MVC框架,必须解决数据的存和取的问题
2.struts2利用值栈来存数据,所以值栈是一个存储数据的内存结构
1、 ValueStack是一个接口,在struts2中使用OGNL表达式实际上是使用实现了ValueStack接口的类 OgnlValueStack,这个类是OgnlValueStack的基础。
2、 ValueStack贯穿整个action的生命周期。每一个action实例都拥有一个ValueStack对象。其中保 存了当前action对象和其他相关对象。
3、 Struts2把ValueStack对象保存中名为struts.valueStack的request域中。
3.把数据存放在值栈中,在页面上利用ognl表达式显示出来
4.ognl框架对象导航语言
5.ognl类图:

值栈的生命周期
1.一次请求;
值栈的内存结构
1.获取值栈路径:


2.说明:
1、 值是一样的,说明只有一个对象
2、 因为有一种是从request域中获取的,所以是一次请求
3内存结构:
1.大致图:


2.详细图:

说明:
_root:(CompoundRoot) _values:(HashMap)这里存放request、response、session、application等servlet容器
2._root图的放大:

说明:和ValueStack中的root是一致的
值栈的内存总图:

说明:从上图中可以看出valueStack总共分为两个部分: 对象栈:root Map栈:_values
1.1.1 OgnlContext组织结构

1.1.1.1 _values
从上述可以看出,OgnlContext实际上有一部分功能是Map。所以可以看出_values就是一个Map属性。而运行一下下面的代码就可以看到:
|
在_values的map中:
|
key |
value |
|
application |
ApplicationMap |
|
request |
RequestMap |
|
action |
自己写的action |
|
com.opensymphony.xwork2.ActionContext.session |
SessionMap |
而request中的值为:
|
可以看出在程序中存放在request作用域的值被放入到了_values的request域中。
而com.opensymphony.xwork2.ActionContext.session的值为:
|
从上图中可以看出在程序中被加入到session的值在_values中也体现出来。
1.1.1.2 _root
从图中可以看出_root实际上CompoundRoot类,从类的组织结构图中可以看出,这个类实际上是继承了ArrayList类,也就是说这个类具有集合的功能。而且在默认情况下,集合类的第一个为ValueStackAction,也就是我们自己写的action。
存放数据到值栈
1.1对象栈的存放:
方法1:
Push方法:


方法二:
add方法:

1.2.对象栈的提取(栈顶):


从上图可以看出,peek方法为获取对象栈的栈顶元素;

这行代码也可以获取对象栈的栈顶元素;
1.3.对象栈元素的弹出:


1.4.操作对象栈中的对象:

说明:可以利用valueStack的setParameter方法改变对象栈中的属性值;
关于对象栈:

从图中可以看出来,对象栈的属性来自于对象栈中的每一个对象的属性的get方法;
把一个对象放进对象栈中:

在<s:dubug></s:dubug>中:

如果在对象栈中存在两个相同的属性:案例:两个name


上述的两个类中都同时有name属性,而且当前请求的action在对象栈中
利用该方法就把person对象放入到栈顶了
可以利用上述的方法改变对象栈中的name属性,但是从上往下找,只要找到一个,改变了值就完事了。
2.Map栈的存放:

改方法将一个字符串放到map栈中的request域中;

通过该方法可以把一个字符串放入到map栈中的application域中

通过该方法可以把一个字符串放入到map栈中
Threadlocal
1.本地线程类;
2.可以存放一个对象;
3.Threadlocal对象只能当前线程访问,不能被其他线程访问;
4.传递参数;
案例:


说明:通过参数的传递,一个字符串很容易的从客户端传递到tl1中的tl1方法,再传递到tl2中的tl2方法
2.



说明:
在TL1Super中用到一个字符串
在TL2Super中用到同样的字符串,但是这两个类是完全松耦合,这个时候要用
Threadlocal传递数据,因为不管是否是送耦合的,但是可以肯定在同一个线程中。所以可以得到同一个线程threadlocal对象中的数据
显示值栈中的数据
6.1:Ognl标签:
1.导入标签库:

2.标签库位置:

3.action类:

4.作用:可以通过网页的形式把valueStack中值提取出来;
6.1.2:Property标签:输出标签
属性分析:value: 指定要输出的内容;如果在对象栈中直接指定属性也可以直接调用方法(该对象的方法);如果在Map栈中,用 # 指定,如果value属性不写,则默认输出栈顶的元素;
一:访问对象栈中的元素:

页面上:

把一个对象放入到对象栈中,可以利用s:prototype标签的value属性直接访问
该对象的属性。如果对象栈中出现两个相同的属性,则prototype标签的value属性会
从栈顶往下查找,直到找到为止。赋值的为第一个。

把一个对象放入到map中,再把map放入到对象栈中,在页面上可以利用



二:利用该标签访问Map栈中的元素:
注意:加 # 号访问


Map栈元素的访问:

6.1.3:Iterator标签:迭代器标签
1.用途:主要用于迭代,可以迭代List,Set,Map,Array
2.案例:
1.把集合放到对象栈中:

开始迭代:


2.把List放到map中;

开始迭代:

3.把List放到request域中:


把Map放入到Map栈中:

开始迭代:

5、把List<Map<String,Person>>放入到map栈中


标签属性:
Value:
可选属性,指定被迭代的集合。如果没有设置该属性,则使用对象栈顶的集合。
Var:
可选属性,引用变量的名称
Status:
可选属性,该属性指定迭代时的IteratorStatus实例。该实例包含如下的方法:
int getCount() 返回当前迭代的元素个数
int getIndex() 返回当前迭代元素的索引
boolean isEven() 返回当前迭代元素的索引是否是偶数
boolean isOdd() 返回当前迭代元素的索引是否是奇数
boolean isFirst() 返回当前迭代元素是否为第一个元素
boolean isLast() 返回当前迭代元素是否为最后一个元素
案例:



更多参考api
总结:
1、 如果要迭代一个集合,当前迭代的元素在栈顶
2、 如果要迭代一个map,则当前迭代的元素是entry
3、 Iterator标签中有一个属性status

6.1.4:UI标签:
1、 struts2提供了一套标签机制,由struts2框架把struts2的标签解析成html标签。
2、 在struts2的配置文件中,增加

在struts2的配置文件中加入如下的内容:

由struts2容器反应过来的HTML代码和struts2标签能保持一致;

<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>"> <title>My JSP 'index.jsp' starting page</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
<!--
<link rel="stylesheet" type="text/css" href="styles.css">
-->
</head> <body>
<s:form action="">
<table>
<tr>
<td>用户名</td>
<!--
name为翻译成html的name
cssClass为html的class
-->
<td><s:textfield name="username" cssClass=""></s:textfield></td>
</tr>
<tr>
<td>密码</td>
<td><s:password name="password"/></td>
</tr>
<tr>
<!--
select标签
list 是下拉列表框的数据源
value是默认被选中的值
-->
<td>选择省</td>
<td><s:select list="{'山西省','河南省','四川省'}" value="{'山西省'}"></s:select></td>
<td><s:select list="#{1:'山西省',2:'河南省',3:'四川省'}" value="#{2:'河南省'}"></s:select></td>
<td><s:select list="#provinces" listKey="pid" listValue="name"/></td>
<!--
listKey 为option元素的value属性的值
listValue option元素的文本内容
name
该属性的值用来获取选中的元素
用于回显
--> </tr>
<tr>
<td>选择爱好</td>
<td><s:checkboxlist list="{'足球','篮球','羽毛球'}" value="{'足球'}" name="pid"/></td>
<td><s:checkboxlist list="#{1:'足球',2:'篮球',3:'羽毛球'}" value="#{1:'足球'}" name="pid"/></td>
<!--
list代表数据源
listKey为value值
listValue为显示的值
-->
<td><s:checkboxlist list="#hobbies" listKey="hid" listValue="name" name="hid"/></td>
</tr>
<tr>
<td></td>
<td><s:submit/></td>
</tr>
</table>
</s:form>
</body>
</html>
UI.jsp
6.1.4.2、Select标签:


6.1.4.2、Checkbox标签:


1.1 Form标签

1.1.1 说明
1、 id属性为s:form的唯一标识。可以用document.getElementById()获取。
2、 name属性为s:form的名字,可以用document.getElementsById()获取。
3、 在默认情况下,s:form将显示表格的形式。
1.2 Textfield标签

1.2.1 说明
实际上相当于在表格中多了一行,在这行中多了两列。其变化从上述图中可以很明显的看出。
1.3 Password标签

1.3.1 说明
如果不加showPassword属性,则密码不会显示,把showPassword属性的值设置为true,就能显示密码。
1.4 Hidden标签

1.4.1 说明
Hidden标签并没有加tr和td
1.5 Submit标签
1.5.1 情况一

1.5.2 说明一
这种情况为submit的type属性为submit类型。
1.5.3 情况二:

1.5.4 说明二
这种情况submit的type属性为button.
1.5.5 情况三

1.5.6 说明三
该type类型为image。
1.5.7 综合
以上三种情况说明,当type为submit、button、image都能完成提交。
1.6 Reset标签


1.7 Textarea标签

1.8 Checkbox标签

1.9 Checkboxlist标签
1.9.1 集合为list

1.9.2 集合为map

上述的checkboxlist标签也可以表示为:

listKey相当于<input type=”checkbox”>中的value,listValue相当于label的显示值。
回显:
一. 概念:
在数据提交出现错误的时候, 已填写的信息仍在文本框中, 比如用户登录, 当用户输入错误的密码之后, 用户名仍在文本框, 只是密码框清空
二. 意义:
对于一些要填写很多信息的表单, 如果因为一些错误导致已经填写的整个表单信息重新填写, 对于用户非常地不友好
三. 回显方法:
1. 默认情况:根据属性进行回显
注意:如果把要回显的数据放入到map栈中、则必须根据value进行回显:Value="%{ognl表达式}"
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>"> <title>My JSP 'index.jsp' starting page</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
<!--
<link rel="stylesheet" type="text/css" href="styles.css">
-->
</head> <body>
<s:form action="">
<table>
<tr>
<td>用户名</td>
<!--
s:select s:checkboxlist
这些标签中的list属性的值可以直接跟ognl表达式
一般情况下,表单中的元素的value属性都不能直接跟ognl表达式
可以使用%{ognl表达式}
如果要回显的内容在map栈中,不能根据name属性进行回显,必须用value属性进行回显
<td><s:textfield name="username" value="%{#user.username}"></s:textfield></td>
如果把要回显的对象放入到对象栈中,则页面上可以根据name进行回显
<s:textfield name="username"></s:textfield>
一般情况下,如果要回显一个对象,则把该对象放入到对象栈中
-->
<td><s:textfield name="username"></s:textfield></td>
</tr>
<tr>
<td>爱好</td>
<!--
用name进行回显
-->
<td><s:checkboxlist list="#hobbies" listKey="hid" listValue="name" name="hids"/></td>
</tr>
</table>
</s:form>
</body>
</html>
backview.jsp
1.回显Map栈中数据:

一般情况下,应该把回显的数据放入到对象栈中,因为页面上可以直接根据name进行回显

2. 回显对象栈中数据:

3.回显checkbox:

上述图表示要回显的id值

根据name属性进行回显
代码贴上:
package com.itheima09.struts.action; import java.util.ArrayList;
import java.util.List; import com.itheima09.struts.bean.Hobby;
import com.itheima09.struts.bean.Province;
import com.itheima09.struts.bean.User;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport; public class UIAction extends ActionSupport{
private Long[] hids; public Long[] getHids() {
return hids;
} public void setHids(Long[] hids) {
this.hids = hids;
} private Long pid; public Long getPid() {
return pid;
} public void setPid(Long pid) {
this.pid = pid;
} public String testSelect(){
List<Province> provinces = new ArrayList<Province>();
Province province1 = new Province();
province1.setPid(1L);
province1.setName("山西省");
Province province2 = new Province();
province2.setPid(2L);
province2.setName("河南省"); Province province3 = new Province();
province3.setPid(3L);
province3.setName("四川省"); provinces.add(province1);
provinces.add(province2);
provinces.add(province3); this.pid = 3L; ActionContext.getContext().put("provinces", provinces);
return "ui";
} public String testCheckbox(){
List<Hobby> hobbies = new ArrayList<Hobby>();
Hobby hobby1 = new Hobby();
hobby1.setHid(1L);
hobby1.setName("足球");
Hobby hobby2 = new Hobby();
hobby2.setHid(2L);
hobby2.setName("蓝球"); Hobby hobby3 = new Hobby();
hobby3.setHid(3L);
hobby3.setName("荷兰球"); hobbies.add(hobby1);
hobbies.add(hobby2);
hobbies.add(hobby3); ActionContext.getContext().put("hobbies", hobbies); return "ui";
} /**
* 回显用户名
*/
public String testBackViewUsername(){
User user = new User();
user.setUsername("王二麻子");
user.setPassword("123456");
//ActionContext.getContext().put("user", user);
//ActionContext.getContext().put("username", "aaa");
ActionContext.getContext().getValueStack().push(user);
return "backview";
} /**
* 回显爱好中的值
*/
public String testBackViewHobby(){
/**
* 在页面上把checkbox的内容显示出来
*/
List<Hobby> hobbies = new ArrayList<Hobby>();
Hobby hobby1 = new Hobby();
hobby1.setHid(1L);
hobby1.setName("足球");
Hobby hobby2 = new Hobby();
hobby2.setHid(2L);
hobby2.setName("蓝球");
Hobby hobby3 = new Hobby();
hobby3.setHid(3L);
hobby3.setName("荷兰球");
hobbies.add(hobby1);
hobbies.add(hobby2);
hobbies.add(hobby3);
ActionContext.getContext().put("hobbies", hobbies); /**
* 用于回显
*/
this.hids = new Long[2];
this.hids[0] = 1L;
this.hids[1] = 2L;
return "backview";
}
}
UIaction
拦截器
需求:如果要访问某一个action类中的某一个方法的内容,如果是admin用户则访问,如果不是admin用户则不能访问。
实现:
7.1:编写一个拦截器(步骤):
7.1.1:编写interceptor:

7.1.2:写一个pvilegeSuperAction:

7.1.3:在配置文件中进行配置:

解析:

说明:
通过该图可以看出:当提交一个url请求时,先执行所有的拦截器(按照声明的从上到下的顺序),再执行action。
声明一个拦截器

声明一个拦截器栈

既可以引用一个拦截器

也可以引用一个拦截器栈

真正的做法:


如果p包继承了privilege包就把所有的新的拦截器栈继承过来了,如果没有继承,则执行默认的拦截器栈
缺点
把权限判断的代码和业务逻辑的代码混合在一起了
1.1 实现方案一
|
说明:
1、 这样做,程序的结构不是很好。原因是权限的判断和业务逻辑的方法紧密耦合在了一起。如果权限的判断很复杂或者是业务逻辑很复杂会造成后期维护的非常困难。所以结构不是很好
2、 这种形式只能控制一个action中的一个方法。如果很多action中的很多方法都需要这种控制。会导致大量的重复代码的编写。
1.2 实现方案二
动态代理可以实现。请参见cn.itcast.struts.jdkproxy包下的类。
1.3 实现方案三
在struts2中,用拦截器(interceptor)完美的实现了这一需求。在struts2中,
内置了很多拦截器,在struts-default.xml文件中可以看出。用户还可以自定义 自己的拦截器。自定义拦截器需要以下几点:
1、 在配置文件中:
包括两个部分:声明拦截器栈和使用拦截器栈
|
2、 在拦截器类中
一个类如果是拦截器,必须实现Interceptor接口。
|
案例2.在执行action的方法之前,
1、 开启日志
2、 权限的检查
1步骤:声明两个拦截器:

2.在执行的拦截器栈中,按照执行的先后顺序引入拦截器

注意:1.等我解决了在写QAQ
7.2,拦截器的应用:
7.2.1.属性驱动:

注意事项:
1、 必须使用struts2默认的拦截器栈中的ParametersInterceptor
2、 Action中的属性和表单中的name属性的值保持一致
3、 利用valueStack.setValue方法可以赋值了
模型驱动:
背景:
假设你正在完成一个网站的注册功能。在后台需要得到20多个属性,才能完成注册。如果用action中的属性获取值,就是这样的情况:
1、 在action中会写20个属性
2、 这20个属性添加set和get方法。
说明:这样会导致action中的代码结构不是很好。模型驱动很好的解决了这个问题。


从上图可以看出,ModelDriverInterceptor有两个作用:
1、 当前请求的action必须实现ModelDriver接口
2、 把model对象放入到了栈顶
1.验证:
1.防止表单的重复提交:
2.文件上传:
3.类型转换:
Struts笔记二:栈值的内存区域及标签和拦截器的更多相关文章
- 【Java】「深入理解Java虚拟机」学习笔记(2)- JVM内存区域
一.运行时数据区 JVM在执行Java程序的时候,将其运行时数据区划分为若干不同区域.它们的用途和创建及销毁的时间不同. 1.程序计数器(Program Counter Register) 是一块很小 ...
- 深入理解Java虚拟机学习笔记(一)-----Java内存区域
一 概述 对于 Java 程序员来说,在虚拟机自动内存管理机制下,不再需要像C/C++程序开发程序员这样为内一个 new 操作去写对应的 delete/free 操作,不容易出现内存泄漏和内存溢出问题 ...
- 走进JVM【二】理解JVM内存区域
引言 对于C++程序员,内存分配与回收的处理一直是令人头疼的问题.Java由于自身的自动内存管理机制,使得管理内存变得非常轻松,不容易出现内存泄漏,溢出的问题. 不容易不代表不会出现问题,一旦内存泄漏 ...
- 《深入理解Java虚拟机》笔记--第二章、Java内存区域与内存溢出异常
Java程序员把内存的控制权交给了Java虚拟机.在Java虚拟机内存管理机制的帮助下,程序员不再需要为每一个new操作写对应的delete/free代码,而且不容易出现内存泄露和溢出. 虚拟机在执行 ...
- 【原】Learning Spark (Python版) 学习笔记(二)----键值对、数据读取与保存、共享特性
本来应该上周更新的,结果碰上五一,懒癌发作,就推迟了 = =.以后还是要按时完成任务.废话不多说,第四章-第六章主要讲了三个内容:键值对.数据读取与保存与Spark的两个共享特性(累加器和广播变量). ...
- jvm虚拟机笔记<二> 垃圾回收与内存分配
确定对象已废弃需要两步: 利用可达性分析算法(与GC roots有关联——虚拟机栈中的对象,方法区静态对象,方法区常量对象,本地方法引用的对象)判断是否需要回收. 是否覆盖过finalize方法并执行 ...
- 深入理解Java虚拟机读书笔记(一)- java内存区域和垃圾收集
jvm内存模型如下图 垃圾回收: 方法区: 这部分的垃圾回收性价比低,一般不要求回收,暂认为是永久代 heap:新生代和永久代之分.永久代主要回收废弃常量和无用的类. 垃圾回收算法: 1. 标记-清除 ...
- 二、spring集成ibatis进行数据源事务管理拦截器环境配置
1.dataSource-applicationContext.xml文件配置理解:(spring1.2.8+ibatis1.5.3)1.1)配置数据源 DriverManagerDataSource ...
- JVM 学习笔记二 :JVM内存区域
一.内存分配概述
随机推荐
- c++中sort函数调用报错Expression : invalid operator <的内部原理
当我们调用sort函数进行排序时,中的比较函数如果写成如下 bool cmp(const int &a, const int &b) { if(a!=b) return a<b; ...
- C#中的注释
帮助程序员便于阅读代码 单行注释 // 多行注释 /* * */ 文档注释 /// <summary> /// ... /// <summary>
- python pip 安装常用库如何使用国内镜像源
最近通过python安装一些常用库发现下载速度特别慢,而且还会出现无法安装等错误,尝试找了一些国内的镜像源,发现阿里云的速度很快,这里做个记录,并分享给大家. 国内镜像源 阿里云:http://mir ...
- opencv:图形绘制与填充
#include <opencv2/opencv.hpp> #include <iostream> using namespace cv; using namespace st ...
- 创业学习---《调研黑客上:锁定调研目标》--D-2.调研模块---HHR计划---以太一堂
第一,开始学习: 思考题: (1)你的项目有哪些值得关注的竞争对手?为什么是这些,你是如何分类的? (2)拿出其中一个产品,你会怎么分析他? 第一,<明确调研目标>(补充) 1,调研4大类 ...
- 读书小记--<如何阅读一本书 >
目录 阅读的活力与艺术 基础阅读 检视阅读 分析阅读 主题阅读 阅读与心智成长 之前看到一位科大的博主,在考研期间看了很多书,同时也看了很多课外书籍,TA说希望能够陶冶自己的性情.看到这,我想起了 ...
- selenium配合phantomjs实现爬虫功能,并把抓取的数据写入excel
# -*- coding: UTF-8 -*- ''' Created on 2016年5月13日 @author: csxie ''' import datetime from Base impor ...
- AcWing - 156 矩阵(二维哈希)
题目链接:矩阵 题意:给定一个$m$行$n$列的$01$矩阵$($只包含数字$0$或$1$的矩阵$)$,再执行$q$次询问,每次询问给出一个$a$行$b$列的$01$矩阵,求该矩阵是否在原矩阵中出现过 ...
- zookeeper 源码(一) 选举和同步数据
前言 在开始阅读代码前我们先来了解一下zk 的大致结构,具体大概要实现的核心功能有那些,心中有个大概的框架阅读代码时再深入其中的细节,就会非常好懂,本人觉得这是一个阅读源码的好方法,可以最快地切入到源 ...
- codeforces-1271A - Suits
A. Suits A new delivery of clothing has arrived today to the clothing store. This delivery consist ...