array(2) { ["docs"]=> array(10) { [0]=> array(10) { ["id"]=> string(3) "428" ["text"]=> string(77) "Visual Studio 2017 单独启动MSDN帮助(Microsoft Help Viewer)的方法" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(8) "DonetRen" ["tagsname"]=> string(55) "Visual Studio 2017|MSDN帮助|C#程序|.NET|Help Viewer" ["tagsid"]=> string(23) "[401,402,403,"300",404]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511400964" ["_id"]=> string(3) "428" } [1]=> array(10) { ["id"]=> string(3) "427" ["text"]=> string(42) "npm -v;报错 cannot find module "wrapp"" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(4) "zzty" ["tagsname"]=> string(50) "node.js|npm|cannot find module "wrapp“|node" ["tagsid"]=> string(19) "[398,"239",399,400]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511400760" ["_id"]=> string(3) "427" } [2]=> array(10) { ["id"]=> string(3) "426" ["text"]=> string(54) "说说css中pt、px、em、rem都扮演了什么角色" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(12) "zhengqiaoyin" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511400640" ["_id"]=> string(3) "426" } [3]=> array(10) { ["id"]=> string(3) "425" ["text"]=> string(83) "深入学习JS执行--创建执行上下文(变量对象,作用域链,this)" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(7) "Ry-yuan" ["tagsname"]=> string(33) "Javascript|Javascript执行过程" ["tagsid"]=> string(13) "["169","191"]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511399901" ["_id"]=> string(3) "425" } [4]=> array(10) { ["id"]=> string(3) "424" ["text"]=> string(30) "C# 排序技术研究与对比" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(9) "vveiliang" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(8) ".Net Dev" ["catesid"]=> string(5) "[199]" ["createtime"]=> string(10) "1511399150" ["_id"]=> string(3) "424" } [5]=> array(10) { ["id"]=> string(3) "423" ["text"]=> string(72) "【算法】小白的算法笔记:快速排序算法的编码和优化" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(9) "penghuwan" ["tagsname"]=> string(6) "算法" ["tagsid"]=> string(7) "["344"]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511398109" ["_id"]=> string(3) "423" } [6]=> array(10) { ["id"]=> string(3) "422" ["text"]=> string(64) "JavaScript数据可视化编程学习(二)Flotr2,雷达图" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(7) "chengxs" ["tagsname"]=> string(28) "数据可视化|前端学习" ["tagsid"]=> string(9) "[396,397]" ["catesname"]=> string(18) "前端基本知识" ["catesid"]=> string(5) "[198]" ["createtime"]=> string(10) "1511397800" ["_id"]=> string(3) "422" } [7]=> array(10) { ["id"]=> string(3) "421" ["text"]=> string(36) "C#表达式目录树(Expression)" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(4) "wwym" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(4) ".NET" ["catesid"]=> string(7) "["119"]" ["createtime"]=> string(10) "1511397474" ["_id"]=> string(3) "421" } [8]=> array(10) { ["id"]=> string(3) "420" ["text"]=> string(47) "数据结构 队列_队列实例:事件处理" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(7) "idreamo" ["tagsname"]=> string(40) "C语言|数据结构|队列|事件处理" ["tagsid"]=> string(23) "["246","247","248",395]" ["catesname"]=> string(12) "数据结构" ["catesid"]=> string(7) "["133"]" ["createtime"]=> string(10) "1511397279" ["_id"]=> string(3) "420" } [9]=> array(10) { ["id"]=> string(3) "419" ["text"]=> string(47) "久等了,博客园官方Android客户端发布" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(3) "cmt" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511396549" ["_id"]=> string(3) "419" } } ["count"]=> int(200) } 222 Struts2 (三) — OGNL与值栈 - 爱码网
gdwkong

一、OGNL表达式

1.概述

1.1什么是OGNL

  ​ OGNL是Object-Graph Navigation Language的缩写,俗称对象图导航语言. 它是一种功能强大的表达式语言,通过它简单一致的表达式语法,可以存取对象的任意属性,调用对象的方法,遍历整个对象的结构图,实现字段类型转化等功能。

​   Eg: hibernate 查询方式 : 对象导航查询。

​   其实就是查询出来了一个对象之后,通过对象里面的getXXX() 来获取关联的对象。

​   它是一个开源项目,并不是struts发明出来的,只是struts里面默认的表达式是使用OGNL表达式,也就是说OGNL是struts2的默认表达式语言。

1.2 OGNL作用
  • 获取对象的成员(支持对象方法的调用,支持对象对象属性访问,支持静态方法的调用,支持集合对象操作, )

  • 在配置文件里面使用

  • 进行运算

2.OGNL要素

​   OGNL 有三个核心元素 ,我们只有理解这三个核心元素,才能去更好的学习OGNL表达式

2.1表达式 (Expression)

​   表达式用于表示我们到底想执行什么操作 。 比如:想获取成员 ,直接用对象.成员名字就可以得到该成员的值

2.2 根元素 (Root)

​   OGNL 表述的是对象导航语言、那么必须的指明根元素是什么。 好比我们要在树上找果子、必须指定到底是那一棵树是一样的道理。

2.3上下文 (Context)

​   上下文其实就是我们的那个root寄存的位置,它在内存当中其实就是一个Map 。OGNL 有一个类专门用来表示上下文环境 叫做OGNLContext , 它其实就是一个Map.

  • EG:Map里面存放了很多的User对象, 那么我们必须指明是在哪一个根上面找这些user对象。

  我们通过一个例子来描述这三者之间的关系

  EG: 一个果农在果园摘苹果

    上下文 (Context): 果园, 果园里面有苹果树, 梨树, 桃树....

    根元素 (Root): 苹果树

    表达式 (Expression): 要摘什么样的苹果(大的, 小的, 熟的, 红的....)

3.OGNL入门

  大家在使用OGNL的时候,要在脑海中有一个想像,就是内存中已经有了一个对象,我们需要去操作这个对象,获取里面的成员,或者去调用它的某个方法。那么表达式应该怎么写。这和我们的EL 表达式有异曲同工之妙,都是用于获取对象、然后再关联到具体的某一个成员.当然我们现在给大家在代码里面演示OGNL的一些用法,一般我们使用OGNL 最多的地方还是在jsp页面上。

3.1表达式与根对象【了解】
  • 访问根对象属性

     1 /**
     2  * OGNL表达式和根对象: 访问属性
     3  * @throws OgnlException 
     4  */
     5 @Test
     6 public void  fun01() throws OgnlException{
     7     //根对象
     8     User root = new User("张三", 18);
     9     //表达式(想得到什么)
    10     String expression = "username";
    11     //通过Ognl方式获得根对象中的表达式结果
    12     Object value = Ognl.getValue(expression , root);
    13     System.out.println("value="+value);
    14 }
  • 访问根对象方法

  •  1 /**
     2  * OGNL表达式和根对象: 访问方法
     3  * @throws OgnlException 
     4  */
     5 @Test
     6 public void  fun02() throws OgnlException{
     7     //根对象
     8     User root = new User("张三", 18);
     9     //表达式(想得到什么)
    10     String expression = "getUsername()";
    11     //通过Ognl方式获得根对象中的表达式结果
    12     Object value = Ognl.getValue(expression , root);
    13     System.out.println("value="+value);
    14 }
  • 访问静态方法

  •  1 @Test
     2 //访问静态方法: @类的全限定名@方法名, 并且根对象设置为null
     3 public void fun03() throws Exception {
     4         //double random = Math.random();
     5         //System.out.println(random);
     6         //String expression = "@java.lang.Math@random()";
     7         //Object value = Ognl.getValue(expression , null);
     8         //System.out.println(value);
     9                         
    10         //Runtime runtime = Runtime.getRuntime();
    11         //runtime.exec("calc.exe");
    12         //runtime.exec("shutdown.exe -s -t 3");
    13         String expression = "@java.lang.Runtime@getRuntime().exec(\'calc.exe\')";
    14         Object value = Ognl.getValue(expression , null);
    15 }
3.2表达式和上下文【了解】
  • 结合上下文访问根对象属性

    
    
     1 /**
     2  * 表达式和上下文  在水果园摘水果
     3  * 1.表达式:表达式用于表示我们到底想执行什么操作,想得到什么  eg: 摘桃子 
     4  * 2.根对象:具体操作的对象,得到这个对象某某  eg:桃树
     5  * 3.上下文: 其实就是根对象寄存的位置.其实就是一个Map,也就是说Map里面可以存到很多的对象(根对象和非根对象)
     6  * eg: 果园, 这里有桃树(根对象), 还有苹果树,梨树...
     7  * @throws OgnlException
     8  */
     9 @Test
    10 public void  fun03() throws OgnlException{
    11       User user1 = new User("张三", 18);
    12       User user2 = new User("李四", 19);
    13       //上下文
    14       Map<String, User> context  = new HashMap<String, User>();
    15       context.put("user1", user1);
    16       context.put("user2", user2);
    17         
    18      //根对象(指定user1为根对象)
    19      Object root = user1;
    20         
    21      //表达式(想得到什么)
    22      String expression = "username";
    23      //通过Ognl方式获得根对象中的表达式结果(获得根对象的username的值,也就是user1的)
    24      Object value = Ognl.getValue(expression, context, root);
    25      System.out.println("value="+value);
    26         
    27      //获得非根对象的值(user2的)
    28      expression = "#user2.username";
    29      value = Ognl.getValue(expression, context, root);
    30      System.out.println("value="+value);
    31 }

    ​ 翻译上面的getValue方法的含义是: 在上下文 context里面找到一个 根user1 然后取它的name值

    ​ 如果想要获取user2的数据 因为user2不是根对象,所以要想取它的值,需要写成 #key.name 。 String expression = "#user2.name"

3.3在页面使用Ognl【重点】

​ 要想在页面上(jsp..)使用Ognl, 需要借助struts2的标签才可以使用.

​ 使用标签: <s:property value=\'OGNL表达式\'/>

​ 步骤: 1.导入struts2标签库到页面<%@ taglib uri="/struts-tags" prefix="s" %>

​     2.使用标签<s:property value=\'OGNL表达式\'/>, 把value属性取值所对应的内容输出到浏览器上

  • 调用非静态方法

    <s:property value="\'aaa\'.length()"/>desw
  • 调用静态方法

    默认情况下struts2不允许在页面调用静态方法, 调用之前需要在struts.xml配置常量

    <constant name="struts.ognl.allowStaticMethodAccess" value="true"></constant>
    在jsp页面上使用:
  • <s:property value="@java.lang.Math@random()"/>

二、ValueStack值栈

1.概述

1.1什么是值栈

​   value stack : 翻译过来是值栈的意思。

​   回想以前我们在学习servlet的时候,我们要想 servlet操作完成后,生成了一个集合或者对象数据, 在页面上显示,我们通常的做法是。把这个对象或者集合存储在作用域里面,然后到页面上再取出来。

  strtus 框架是对我们的servlet进行了包装 ,它内部自己也有一套存值和取值的机制 ,这一套机制就是现在说的值栈。

2.值栈创建的时机

2.1 Servlet和Action的区别
  • Servlet: Servlet只会创建一次实例,以后再过来请求,不会创建实例

  • Action: Action是多例,来一次请求就创建一个Action实例。 创建一次Action的实例,就创建一次ActionContext实例,并且就创建出来一个值栈的实例。

2.2什么时候创建值栈
  • 请求到来的时候才会创建值栈, 当来了请求,会执行前端控制器的doFilter方法,在doFilter方法里面,有如下代码 89行

    prepare.createActionContext(request, response);
  • 在createActionContext()这个方法内部

     1 //从ThreadLocal里面获取ActionContext实例,一开始是没有的,所以该对象是 null
     2  ActionContext oldContext = ActionContext.getContext();
     3     if (oldContext != null) {
     4       // detected existing context, so we are probably in a forward
     5       ctx = new ActionContext(new HashMap<String, Object>(oldContext.getContextMap()));
     6     } else {
     7  8       //创建值栈对象  是OgnlValueStack 对象
     9      ValueStack stack = dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack();
    10 11     //给值栈里面的上下文区域存东西 。 存request \ response \session \application...
    12     stack.getContext().putAll(dispatcher.createContextMap(request, response, null));
    13     //创建ActionContext的对象。 然后跟值栈关联上
    14     ctx = new ActionContext(stack.getContext());
    15 }

3.值栈的内部结构

​ 我们查看值栈的内部结构,其实是想研究值栈存值的时候,是存到了什么地方去的。

  

  • 值栈其实就是一个类 OgnlValueStack ,我们如果往值栈存值, 其实是存到了这个类中的两个集合去。

    
    
    CompoundRoot root;  //这其实是一个集合  list
    transient Map<String, Object> context;  //这是OGNL上下文  OGNLContext
    //底下还有一行代码 
     context.setRoot(root);
    //也就是设置了OGNL上下文的根其实就是一个list集合 。  说的具体一点。 就是那个CompoundRoot对象。

  我们使用OGNL表达式取值的时候,都是去上下文里面取值。

  ​ root 区,根对象。具体类型为CompoundRoot。CompoundRoot实现了List接口,其实就是一个list集合。其实我们存值的话都是存在root区域.

​   context 区:上下文对象。具体类型为OgnlContext。OgnlContext内部操控的是一个Map集合,其实context区可以理解为一个Map集合。

4.获得ValueStack

4.1 通过ActionContext对象的实例获得

​ 由于前面创建好值栈后,让它和ActionContext关联上了,所以我们只要通过ActionContext去获取它即可。

  • 获得值栈代码

    ValueStack valueStack = ActionContext.getContext().getValueStack();
4.2通过request域获得ValueStack

​   在执行完我们的Action方法前,struts会把值栈往request对象里面存起来,所以我们使用request对象即可获取到.

  在前端过滤器的doFilter()方法里面有 execute.executeAction(request, response, mapping);这行代码,执行Action.

  具体源码参见: Dispatcher类中的serviceAction方法 , 位于568行行:request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY,   proxy.getInvocation().getStack());所以后续我们也只要使用request对象然后结合对应的STRUTS_VALUESTACK_KEY 这个key即可获取到值栈

  • 获得值栈代码

    ValueStack valueStack = (ValueStack) ServletActionContext.getRequest().getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);

5.值栈存取值

5.1 push方式

​   直接放置在栈顶,没有什么key与之对应。所以取值的话,直接写属性名即可。 但是如果push 了多个,并且还想获取的不是栈顶的值,是栈顶下来的某一个位置的值,那么可以采用[0] \ [1] 这种做法

  

  • 存值Java代码

    User user = new User("admin" ,"10086");
    ValueStack stack = ActionContext.getContext().getValueStack();
    //执行的是压栈的动作。 放置的东西永远会在栈顶。
    stack.push(user);
    ​
    //第二次的时候,user02已经位于栈顶 ,之前的那个user就要往下压一格
    User user02 = new User("zhangsan" ,"1000000");
    stack.push(user02);
  • 取值(演示[0]和[1]的区别)

    取push放的值<br>
    <s:property value="username"/> , <s:property value="password"/><br><br>取push的值 就取第二个,不取栈顶<br>
    <s:property value="[1].top.username"/>  , <s:property value="[1].password"/><br>
5.2set方式

​   set 和 push 的区别在于, push 是直接把数据压倒栈顶 , 但是set方式是用一个map集合来包装数据,然后才把map压倒栈顶。 所以取值手法也有点不一样。 set(key, value); key就是包装map的key。

  • 存值Java代码

    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>
5.3属性驱动方式

​   和之前的获得请求参数属性驱动类似. 存到了当前的Action类的区域(还是在根里面)

  • 存值Java代码

     1 public class ActionDemo01 extends ActionSupport {
     2     private String str;
     3     public String fun01(){
     4         str = "hello";
     5         return "success";
     6     }
     7     //提供get方法
     8     public String getStr() {
     9         return str;
    10     }
    11 12 }
  • 取值

  • 取属性封装的值<br>
    <s:property value="str"/>
5.4模型驱动方式
  • 存值

  •  1 public class ActionDemo01 extends ActionSupport implements ModelDriven<User> {
     2     private User user;
     3     public String fun01(){
     4         return "success";
     5     }
     6     @Override
     7     public User getModel() {
     8         if(user == null){
     9             user = new User("李四", "66666666");
    10         }
    11         return user;
    12     }
    13 14 }
  • 取值

    <s:property value="model.username"/> , <s:property value="model.password"/><br>
    或者:
    <s:property value="username"/> , <s:property value="password"/><br>

    由于使用模型驱动封装,存值的时候,也还是存到action的范围里面去.

5.5使用EL表达式取值

​   EL表达式也可以取到值栈的值,本来EL表达式是用于取作用域的值,但是值栈和作用域是两个东西。 为什么EL 表达式也能去值栈的值呢?原因是 : struts对EL 表达式取值的代码进行了扩展,如果从作用域取不到值就会去值栈找。

​   reuqest.setAttribute("user" , user); ${user.name} ------> pageContext.findAttrbute(); --> 先从page范围找, 没有,就找request, 还没有就找session。request类型被包装成了 StrutsRequestWrapper 在里面的getAttribute 做了判定,如果从作用域中去不到值,就去值栈取值

6.OGNL中的符号

6.1#号的作用
  • 获取Context中的数据,非根对象里面的数据

    <s:property value="#request.name"/>
    <s:property value="#session.name"/>
  • 构建一个Map集合

    //var变量会存一份到root也会存一份到上下文 c:foreach
    <s:iterator var="entry" value="#{ \'aa\':\'11\',\'bb\':\'22\',\'cc\':\'33\' }"> 
    <s:property value="key"/>--<s:property value="value"/><br/>
    <s:property value="#entry.key"/>--<s:property value="#entry.value"/><br/>
    </s:iterator>
6.2 $号的作用
  • 在xml等配置文件里面使用ognl

 

6.3 OGNL取值流程图

分类:

技术点:

相关文章: