Day43-Struts03
来源:互联网 发布:windows开启snmp 编辑:程序博客网 时间:2024/06/05 11:19
一、OGNL - 对象图导航语言
Object-Graph Navigation Language
其实就是给定一个对象,然后可以获取这个对象身上的属性值, 也可以给这个对象身上的属性赋值。 也可以调用这个对象方法…
struts底层就是用OGNL表达式来进行赋值的。
1. OGNL 核心元素
* 表达式:用于表示要执行什么操作* 根元素:root,OGNL会在一定的范围里面查找对象,标注根对象的好处就是便于OGNL区分对象的位置,快速的定位它们的所在,而且OGNL在获取根对象和非根对象的表达式也不尽相同。* 上下文:在OGNL范围里面,就是一个map集合,表示的就是一个范围,限定我们查询的范围。
2. OGNL 入门演示
1)导入Ognl的jar包
其实在struts基本jar包里面包含了Ognl的jar包;
核心jar包:ognl-3.0.19.jar;
依赖的jar包:javassist-3.11.0.GA.jar
2)Ognl的静态方法:
Ognl.setValue(表达式,根对象,值);
Ognl.getValue(表达式,根对象)
参数1:表达式,就是要操作什么东西,属性或者方法名
参数2:根对象:前面的这个name属性值就是从根对象取出来的。
Ognl可以操作属性,也可以操作方法!
@Test public void test01() throws OgnlException{ User user1 = new User("张三","123"); User user2 = new User("李四","123"); System.out.println(Ognl.getValue("username", user2)); System.out.println(Ognl.getValue("getUsername()", user2)); //这个代码虽然没有指定map集合,但是猜测。底下会有一个map上下文来包装我们的对象 Ognl.setValue("username", user2, "王五"); System.out.println(Ognl.getValue("getUsername()", user2)); System.out.println(Ognl.getValue("getUsername()", user1)); }
- 表达式 和 根对象 和 上下文(OGNLContext )
其实OGNL 里面表示上下文是有一个具体类来表示的。这个类是OGNLContext , 其实这个类也没有多少特别之处,就是一个Map 。
@Test public void test02() throws OgnlException{ User user1 = new User("张三","123"); User user2 = new User("李四","123"); Map<String, User> map = new HashMap<String, User>(); map.put("user1", user1); map.put("user2", user2); /* * 参数一: 表达式 * 参数二: 上下文 ,其实就是一个范围 * 参数三: 根对象 * * #取非根对象上的属性,就必须在前面加 # * OGNL默认找属性都是在根对象身上找的。如果不加#就表示要在根对象上找属性。 */ System.out.println(Ognl.getValue("username", map, user1)); System.out.println(Ognl.getValue("#user2.username", map, user1)); }
二、值栈(重点)– Value Stack
值栈其实就是一个类 OgnlValueStack
它的作用就是存值,有点类似以前servlet阶段的 request 和 session这些作用域。 主要功能就是存值。我们要向从值栈取值。 配合struts标签来取的话,需要使用OGNL表达式。
1. Servlet 和 Action区别
servlet:
只会创建一次实例,以后再过来请求,不会创建实例
action:
action是多实例,来一次请求就创建一次实例。创建一次Action的实例,就创建一次ActionContext实例,并且就创建出来一个值栈的实例,并且是一一对应的。
action是服务器启动或者是项目加载的时候初始化,执行init()方法
2. 值栈创建的时机
请求到来的时候才会创建值栈。
翻翻sturts2的源码配置核心过滤器的中有个类StrutsPrepareAndExecuteFilter,这个类的doFitler里面有行关键代码
prepare.createActionContext(request, response);
该方法的背后有几行关键性的代码:
//从ThreadLocal里面获取ActionContext实例,一开始是没有的,所以该对象是 null ActionContext oldContext = ActionContext.getContext(); if (oldContext != null) { // detected existing context, so we are probably in a forward ctx = new ActionContext(new HashMap<String, Object>(oldContext.getContextMap())); } else { //创建值栈对象 是OgnlValueStack 对象 ValueStack stack = dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack(); //给值栈里面的上下文区域存东西 。 存request \ response \session \application... stack.getContext().putAll(dispatcher.createContextMap(request, response, null)); //创建ActionContext的对象。 然后跟值栈关联上 ctx = new ActionContext(stack.getContext()); }
3. 值栈的内部结构
值栈存值的时候,是存到了什么地方去的。
值栈其实就是一个类 OgnlValueStack ,我们如果往值栈存值, 其实是存到了这个类中的两个集合去。
CompoundRoot root; //这其实是一个集合 list transient Map<String, Object> context; //这是OGNL上下文 OGNLContext
源码中有一行代码
context.setRoot(root);
也就是设置了OGNL上下文的根其实就是一个list集合 。就是那个CompoundRoot对象。
我们使用OGNL表达式取值的时候,都是去上下文里面取值。
contextMap(也就是上表中的OgnlContext)内部维护 了值栈的引用,同时,contextMap内部也维护了root的引用。
值栈的contextMap内部维护了值栈本身(OgnlValueStack)以及根(ComponentRoot)的引用.实际上,也可以看出来值栈的contextMap的实际数据结构为OgnlContext.
同时:contextMap内部加入了各个域对象(request,ServletContext,session)的引用.通过contextMap可以获取到各个域对象的引用.
捋一捋关于值栈、ActionContext、及其他:
首先,当服务器创建的时候,web.xml里面配置的过滤器的类StrutsPrepareAndExecuteFilter就执行init方法进行初始化。
每次请求到来的时候,都会经过过滤器,执行过滤器的dofilter方法。每来一个请求,就会创建一个Action实例,就会创建一个ActionContext的实例,在ActionContext创建实例时,又会创建一个值栈ValueStack的实例,ValueStack其实是ActionContext创建的时候创建的。
值栈ValueStack的实例的类是OgnlValueStack,值栈里面主要有CompoundRoot和contextMap,其中,CompoundRoot本质上是一个ArrayList,而contextMap本质上是一个Map。另外,contextMap里面还添加了原来servlet里面的request、response、application等等对象
其中contextMap维护了值栈本身和root的引用.ActionContext内部维护了contextMap的引用
4. 值栈的存值 & 取值
方式一:push方式
- push – 执行的是压栈的动作。 放置的东西永远会在栈顶。
直接放置在栈顶,没有什么key与之对应。所以取值的话,直接写属性名即可。 但是如果push 了多个,并且还想获取的不是栈顶的值,是栈顶下来的某一个位置的值,那么可以采用[0] \ [1] 这种做法
[0] : 从栈顶开始包含,在这段范围取值
[1] : 从第二层开始包含,在这段范围取值
案例:
action:(注意,在配置struts.xml的时候不能够采用重定向,只能够采用请求转发的方式,否则存入到值栈的数据会丢失)
public class ActionGetParameter extends ActionSupport{ public String test(){ System.out.println("test方法执行了"); User user01 = new User("zhangsan","123"); User user02 = new User("李四","456"); ValueStack valueStack = ActionContext.getContext().getValueStack(); //执行的是压栈的动作。 放置的东西永远会在栈顶。 valueStack.push(user01); valueStack.push(user02); ServletActionContext.getRequest().setAttribute("address", "深圳"); return SUCCESS; }}
页面:
页面需要引入strtus的标签库
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%><%@ taglib uri="/struts-tags" prefix="s"%><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title>Insert title here</title></head><body> 这是跳转后的页面 <br /> //如果不写value,只写成<s:property />,那么就会直接输出栈上面的第一个对象。应该是地址值 <s:property value="username" /> -- <s:property value="password" /> <br /> <s:property value="[1].username" /> <s:property value="[1].top.username" /> -- <s:property value="[1].password" /> <!-- 如果是多个同样的对象,就采用这样的方式取出--> <br /> <s:debug></s:debug></body></html>
方式二 – set方式
set 和 push 的区别在于, push 是直接把数据压倒栈顶 , 但是set方式是用一个map集合来包装数据,然后才把map压倒栈顶。 所以取值手法也有点不一样。
存值:action中的写法
User user = new User("admin" ,"10086"); ValueStack stack = ActionContext.getContext().getValueStack(); stack.set("user01", user);
取值: 页面写法
取set放的值<br> <s:property value="user01.username"/> , <s:property value="user01.password"/><br>
第三种 – 属性封装 – 属性驱动方式
属性驱动方式最常用
需要声明成员变量,提供变量的get方法。
注意:这种方式存储的位置:
存值 :
public class ActionGetParameter extends ActionSupport{ private User user; public User getUser() { return user; } public String test(){ System.out.println("test方法执行了"); user = new User("王五","4444"); return SUCCESS; }}
取值:页面
<s:property value="user.username"/> , <s:property value="user.password"/><br>
第四种 – 模型驱动封装
注意:
存储的位置
存储的属性名为model
存值:
public class ActionDemo extends ActionSupport implements ModelDriven<User>{ private User user; @Override public User getModel() { return user; } public String add(){ user= new User("telangpu","70"); return SUCCESS; }}
取值:
由于使用模型驱动封装,存值的时候,也还是存到action的范围里面去,但是对应的那个属性名就不是user了 而是 model 。
<s:property value="model.username"/> , <s:property value="model.password"/><br>
5. 使用EL表达式取值栈的值。
EL表达式也可以取到值栈的值,本来EL表达式是用于取作用域的值,但是值栈和作用域是两个东西。 为什么EL 表达式也能去值栈的值呢?
原因是 : struts对EL 表达式取值的代码进行了扩展(装饰者模式),如果从作用域取不到值就会去值栈找。当调用request.getAttribute()时,首先会从request范围的数据里找,然后从ValueStack(值栈的root对象里面)里找,最后到StackContext(OgnlContext里面)里找。
reuqest.setAttribute("user" , user); ${user.name} ------> pageContext.findAttrbute(); --> 先从page范围找, 没有,就找request, 还没有就找session。 request类型被包装成了 StrutsRequestWrapper 在里面的getAttribute 做了判定,如果从作用域中去不到值,就去值栈取值。
6. OGNL表达式符号
6.1) # 取非根对象的属性,就需要用到#了。
action:
ServletActionContext.getRequest().setAttribute("user", new User("非根下面的对象","2222"));页面:<s:property value="#request.user.username"/> ---<s:property value="#request.user.password"/>
6.2)% 强制解析字符串成OGNL表达式 %{表达式}
//强制不解析<s:textfield value="%{'#request.user.username'}"></s:textfield><br/>//强制解析<s:textfield value="%{#request.user.username}"></s:textfield>
- Day43-Struts03
- Struts03-struts配置
- day43(4.23)
- Day43-Struts01
- Day43-Struts02
- day43 spring aop_2
- day43 Sping AOP @AspectJ
- day43(4.23)累加
- DAY43 IO模型
- Day43: Miss a job interview
- day43(4。23)打印偶数
- python学习—Day43—多线程
- 达内学习日志Day43:AddAccountServlet(通过JDBC连接)
- js day43 Jquery入门(回顾js,Jquery选择器,dom操作)
- DAY43 Event对象、队列和多进程基础
- js实现键盘操作对div的移动或改变-------Day43
- 《从零开始学Swift》学习笔记(Day43)——构造函数继承
- 【Day43】PHP中用PDO查询Mysql来避免SQL注入风险的方法
- Linux 搭建 Java 环境
- 线性表的插入删除
- poj1064(二分)Cable master
- js调试工具
- HashMap/HashTable
- Day43-Struts03
- JQuery点击弹出窗口外侧,关闭弹出窗口功能
- 数字图像的傅里叶变换笔记
- Codeforces Round #427 (Div. 2) D.Palindromic characteristics
- Eclipse 配置之Java代码模板
- MySQL备份工具xtrabackup原理及实施
- php 下载 xlsx
- 添加XY离散点数据
- D的小L (南阳理工oj 题目366)