struts2默认ognl,值栈

来源:互联网 发布:d810调焦软件 编辑:程序博客网 时间:2024/05/22 15:49

1.OGNL概述
1.1 什么是OGNL?
OGNL(object graph Navigation language) 对象图形导航语言,它是一款简单便捷的语言,通过它的表达式,可以轻松的存取对象的属性和调用对象的方法,遍历整个对象的结构图(说白了就是可以用’对象.属性’获取值)

1.2 它是struts2开发的吗?
OGNL是一个开源项目,并不是struts2开发的,struts2只是使用OGNL作为默认的表达式语言而已.

1.3 OGNL 有什么作用?

  1. OGNL可以获取对象的成员(支持对象方法的调用,支持对象对象属性访问,支持静态方法的调用,支持集合对象操作)
  2. 进行运算

2.OGNL三要素

  • 基本语法
    我们通常通过对象.属性名就可以拿到对应的值
  • 根元素(Root)
    OGNL表述的是对象导航语言,那么必须指明根元素
  • 上下文(Context)
    上下文跟Root存放在一个map里面,Map里面存放了很多的User对象, 那么我们必须指明是在哪一个根上面找这些user对象。

我们通过一个例子来讲述三者之间的区别,
Eg:

就好比如一个果农在果园摘苹果

果园里有很多的树,除了果农在摘苹果的那颗树,其它都是都是上下文(Context),而果农摘的那棵树就相当于根元素(Root),而决定果农摘什么样的苹果(大的,小的,还是熟的) 就是Ognl的语法


3.OGNL入门

3.1表达式与根对象
- 访问根对象属性
准备工作,创建一个JavaBean
既然提到了JavaBean,那么我们来讲讲什么是JavaBean?

javaBean 不是大公司定的规范,也不是一种功能,而是程序员之间的一种规则,只要按照这种规则就,就能实现某种功能等,
JavaBean 编写规则

  • 提供空参构造方法
  • 提供set/get 方法
  • 实现序列化接口
    那么大家知道什么是属性么?属性是成员变量吗?
    答案 如果使用工具生成set/get方法,就可以理解成属性.如果不是,那么属性就是set,get方法去掉 set , get 并且开头字母小写,那么这个字符串就是属性.好了回归正题,来看我们的代码
public class User {    private String name;    private int age;    //提供set/get 方法   属性 去掉get  开头小写 name    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public int getAge() {        return age;    }    public void setAge(int age) {        this.age = age;    }    public User(String name, int age) {        super();        this.name = name;        this.age = age;    }    public User() {    }}

然后创建一个测试类

public class demo1 {    @Test    public void fun_01(){        try {            User user = new User("zs",18);            //使用ognl             User root = user; //根元素            String expression = "name"; //ognl 表达式            Object value = Ognl.getValue(expression,root);            System.out.println(value);        } catch (OgnlException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }    }}

输出结果如下
zs

3.2 访问根对象中的方法

@Test    public void fun_02(){        try {            User user = new User("zs",18);            //使用ognl             User root = user; //根元素            String expression = "getName()"; //ognl 表达式            Object value = Ognl.getValue(expression,root);            System.out.println(value);        } catch (OgnlException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }    }

输出结果 zs
只要表达式跟方法名一致就能调用,不要少了括号

3.调用静态方法
静态方法相信大家都有用过吧,比如Runtime,嘿嘿嘿你懂的
我们就随便使用一个Math来演示好了

@Test    public void fun_03(){        try {            //调用静态方法不需要根元素,直接传null就行            //调用静态方法  规则 需要 @号开头,接上静态方法的全局类路径 列如(cn.itcast.demo2.demo2) 然后再接一个@最后接上需要调用的方法             String expression = "@java.lang.Math@random()"; //ognl 表达式  Math 的类路径            Object value = Ognl.getValue(expression,null);            System.out.println(value);        } catch (OgnlException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }    }

输出结果 0.5854924290246956


模拟上下文访问根对象属性

@Test    public void fun_04(){        try {            //创建一个map  Context 实际上就是一个map            Map<String,User> context = new HashMap<String,User>();            User user = new User("zs",18);  //根元素            User user1 = new User("ls",19);            context.put("user", user);            context.put("user1", user1);            String expression = "name"; //ognl 表达式   取根元素的值                 Object value=Ognl.getValue(expression,context, user);            System.out.println(value);        } catch (OgnlException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }    }

输出结果 zs

那么问题来了,我们要怎么获取非根元素的值呢?
使用”#对象.属性”的方法就可以获取

@Test    public void fun_05(){        try {            //创建一个map  Context 实际上就是一个map            Map<String,User> context = new HashMap<String,User>();            User user = new User("zs",18);  //根元素            User user1 = new User("ls",19);            context.put("user", user);            context.put("user1", user1);            String expression = "#user1.name"; //ognl 表达式   取非根元素的值  user1              Object value=Ognl.getValue(expression,context, user);            System.out.println(value);        } catch (OgnlException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }    }

这就是OGNL的基本使用,

ValueStack值栈

下面我们来讲讲Struts2中的值栈,ValueStack 翻译过来就是值栈的意思,经常使用servlet的人会知道,我们通过传递给页面数据时通常会使用作用域. 既然讲到了作用域,那我就再来再插一嘴把 ….

请求转发,重定向介绍
大家知道什么样的情况下使用请求转发,什么使用重定向么?
我在网上翻了很多帖子,很多人都是这样认为的.
只要有数据传递,那么就是用请求转发,没有数据就使用重定向
如果你是这样,那么我告诉你,你错了,而且错的很离谱
开发中如果这样使用,那么会出大问题的
请求转发应该只在查询中使用,切记 update insert delete 千万不能用转发,因为转发后的路径不会变,想必大家都知道的把 , 那么如果我们是insert 转发的,那么我们只要一刷新,就又插入一条数据,那么不就炸锅了?
查询如果使用重定向,那么只能把值存在session域中.session的默认生命是三十分钟,那么在三十分钟类,用户拿到的数据都不会改变,
影响用户体验 ,,

我们回到value stack 值栈中
值栈创建的时机
1.Servlet 跟 action 的区别

  • Servlet 是 单例多线程 但是 Action是多例的,相对会安全些,可以使用成员变量,
  • Servlet 只会创建一次,Action 每次请求到达都会创建一个新的实例

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

prepare.createActionContext(request, response);//下面是createActionContext()这个方法的内部//从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.值栈的内部结构
其实值栈的内部 就是一个list根元素 Root 一个Map 上下文Context

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

需要注意的是,当我们往值栈存值的时候,值存到了root里面,当我们往request ,等中存值,存入的是上下文, 取值的方式不一样.

还要很重要的一点就是 ,Context 上下文里面 又包含了值栈, 没有死循环,互相持有各种的引用,也就意味着我们只要拿到任何一个,都能取到对方. 在开发中,我们通常会使用Context 来获取值栈

我们取值是从上下文里面取的,因为上下文包含值栈,而 值栈 里有根元素,上下文 ,也就相当于有了全部数据 下面是值栈分析图

这里写图片描述

最后我们来讲讲获取值栈的方式

1.通过request获取,固定写法 方法参数源码里有

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

2.通过ActionContext

ValueStack valueStack = ActionContext.getContext().getValueStack();

好啦,今天就到这里啦,如果有什么不对欢迎留言指出,蟹蟹