美文网首页
SSH框架之Struts2进阶OGNL与值栈详解(三)

SSH框架之Struts2进阶OGNL与值栈详解(三)

作者: Seapp | 来源:发表于2019-04-15 11:16 被阅读0次

    第一节:OGNL详解

    1.1 OGNL概述:

    image.png

    OGNL的全称是对象图导航语言(Object-Graph Navigation Language),它是一种功能强大的开源表达式语言,使用这种表达式语言,可以通过某种表达式语法,存取Java对象的任意属性,调用Java对象的方法,同时能够自动实现必要的类型转换。如果把表达式看作是一个带有语义的字符串,那么OGNL无疑成为了这个语义字符串与Java对象之间沟通的桥梁。

    1.2 OGNL的作用:

    Struts2默认的表达式语言就是OGNL,它具有以下特点:

    • 支持对象方法调用。例如:objName.methodName();
    • 支持类静态方法调用和值访问,表达式的格式为
    @[类全名(包括包路径)]@[方法名 | 值名]。
    @java.lang.String@format('foo%s','bar')。
    
    • 支持赋值操作和表达式串联。例如:price = 100,discount = 0.8,calculatePrice(),在方法中进行乘法计算会返回80。
    • 访问OGNL上下文(OGNL context)和ActionContext。
    • 操作集合对象

    1.3 OGNL的要素:

    OGNL的操作实际上就是围绕着OGNL结构的三个要素而进行的,分别是表达式(Expression)、根对象(Root Object)、上下文环境(Context),下面分别讲解这三个要素,具体如下:

    • 表达式:
      表达式是整个OGNL的核心,OGNL会根据表达式去对象中取值。所有OGNL操作都是针对表达式解析会进行的。它表明了此次OGNL操作要“做什么”。表达式就是一个带有语法含义的字符串,这个字符串规定了操作的类型和操作的内容。OGNL支持大量的表达式语法,不仅支持这种“链式”对象访问路径,还支持在表达式中进行简单的计算。
    • 根对象(Root):
      Root对象可以理解为OGNL的操作对象,表达式规定了“做什么”,而Root对象则规定了“对谁操作”。OGNL称为对象图导航语言,所谓对象图,即以任意一个对象为根,通过OGNL可以访问与这个对象关联的其他对象。
    • Context对象:
      实际上OGNL的取值还需要一个上下文环境。设置了Root对象,OGNL可以堆Root对象进行取值或写值等操作。Root对象所在环境就是OGNL的上下文环境(Context)。上下文环境规定了OGNL的操作“在哪里进行”。上下文环境Context是一个Map类型的对象,在表达式中访问Context中的对象,需要使用“#”号加上对象名称,即“#对象名称”的形式。

    1.4 OGNL入门实践

    • 使用OGNL访问对象的方法:
     @Test
        public void OgnlDemo() throws OgnlException {
            OgnlContext ognlContext = new OgnlContext();
            Object value = Ognl.getValue("'helloworld'.length()", ognlContext, ognlContext.getRoot());
            System.out.println(value);
        }
    

    上述我们调用了字符串的length方法,其中'helloworld'.length()就是OGNL的表达式,最终的输出结果为9
    OGNL除了可以访问对象的方法,还可以访问对象的静态方法。其语法格式如下:

    @类的全路径名@方法名称(参数列表)
    @类的全路径名@属性名称
    //具体示例如下
     @Test
        public void OgnlDemo03() throws OgnlException {
            OgnlContext ognlContext = new OgnlContext();
            ognlContext.put("name","张三");
            String value = (String)Ognl.getValue("#name", ognlContext, ognlContext.getRoot());
            System.out.println(value);
        }
    
        @Test
        public void OgnlDemo04() throws OgnlException {
            OgnlContext ognlContext = new OgnlContext();
            User user = new User();
            user.setName("李四");
            ognlContext.setRoot(user);
            String name = (String)Ognl.getValue("name", ognlContext, ognlContext.getRoot());
            System.out.println(name);
        }
    

    第二节:值栈的详解

    2.1 值栈的概述:

    ValueStack是Struts的一个接口,字面意思是值栈,OgnlValueStack是ValueStack的实现类,客户端发起一个请求Struts2框架会创建一个action实例同时创建一个OgnlValueStack值栈实例,OgnlValueStack贯穿整个Action的生命周期,struts2中使用OGNL将请求Action的参数封装为对象存储的值栈中,并通过OGNL表达式读取值栈中的对象属性值。

    2.2 值栈的内部结构:

    • Context:即OgnlContext上下文,它是一个map结构,上下文中存储了一些引用,parameters、request、session、application等,上下文的Root为CompoundRoot。
    OgnlContext中的一些引用:
      * parameters:该Map中包含当前请求的请求参数。
      * request:该Map中包含当前Request对象中的所有属性。
      * session:该Map中包含当前Session对象中的所有属性。
      * application:该Map中包含当前application对象中的所有属性
      * attr:该Map按如下顺序来检索某个属性:request,session,application
    
    • CompoundRoot:存储了action实例,它作为OgnlContext的Root对象。
      CompoundRoot继承ArrayList实现压栈和出栈功能,拥有栈的特点,先进后出,后进先出,最后压进栈的数据在栈顶。我们把它称为对象栈。
      struts2对原OGNL作出的改进就是Root使用CompoundRoot(自定义栈),使用OnglValueStack的findValue方法可以在CompoundRoot中从栈顶向栈底查找对象的属性值。
      CompoundRoot作为OgnlContext的Root对象,并且在CompoundRoot中action实例位于栈顶,当读取action的属性值时会先从栈顶对象中找对应的属性,如果找不到则继续找栈中的其它对象,如果找到则停止查找。

    2.2 ActionContext和ValueStack的关系:

     public ActionContext createActionContext(HttpServletRequest request, HttpServletResponse response) {
            Integer counter = 1;
            Integer oldCounter = (Integer)request.getAttribute("__cleanup_recursion_counter");
            if (oldCounter != null) {
                counter = oldCounter + 1;
            }
    
            ActionContext oldContext = ActionContext.getContext();
            ActionContext ctx;
            if (oldContext != null) {
                ctx = new ActionContext(new HashMap(oldContext.getContextMap()));
            } else {
                ValueStack stack = ((ValueStackFactory)this.dispatcher.getContainer().getInstance(ValueStackFactory.class)).createValueStack();
                stack.getContext().putAll(this.dispatcher.createContextMap(request, response, (ActionMapping)null));
                ctx = new ActionContext(stack.getContext());
            }
    
            request.setAttribute("__cleanup_recursion_counter", counter);
            ActionContext.setContext(ctx);
            return ctx;
        }
    

    通过上述源码分析:

    • 在创建ActionContext的时候,创建ValueStack的对象,将ValueStack对象给ActionContext。
    • ActionContext中有一个ValueStack的引用,ValueStack中也有一个ActionContext的引用。
    • ActionContext获取ServletAPI的时候,依赖值栈。

    2.3 获取值栈对象

     //获取值栈对象
            //1。通过ActionContext获取valueStack对象
            ValueStack valueStack = ActionContext.getContext().getValueStack();
            //2。通过request获取valueStack对象
            ValueStack valueStack1 = (ValueStack)ServletActionContext.getRequest()
                    .getAttribute(ValueStack.VALUE_STACK);
    

    2.4 操作值栈:

    • 对Action中的属性提供get方法的方式:
      因为Action本身在值栈中,Action中的属性也就默认在值栈中,所以我们可以通过对Action的属性提供get方法的方式来操作值栈。
    • 手动操作值栈:
      调用值栈的push和set方法对值栈进行操作:
    //编写Action类,在方法中获取值栈,并通过set或push方法向值栈中存储数据(这些数据被存储在root区域)
    public class ValueStackDemo extends ActionSupport {
    
        @Override
        public String execute() throws Exception {
            //获取值栈对象
            //1。通过ActionContext获取valueStack对象
            ValueStack valueStack = ActionContext.getContext().getValueStack();
            //2。通过request获取valueStack对象
            ValueStack valueStack1 = (ValueStack)ServletActionContext.getRequest()
                    .getAttribute(ValueStack.VALUE_STACK);
            //3。存入数据
            User user = new User();
            user.setName("张三");
            user.setAge(19);
            valueStack.push(user);
    
            valueStack.set("username","李四");//创建一个Map集合,将该集合存放到栈顶。
    
            return SUCCESS;
        }
    }
    

    返回结果界面中debug查看值栈中的内容,并打印获取到存入值栈中的数据:

    <s:debug></s:debug></br>
    <s:property value="name"/></br>
    <s:property value="age"/></br>
    <s:property value="username"/></br>
    

    2.5 获取数据

    • 在root区域获取数据:不需使用 “#”号
    <s:property value="name"/></br>
    <s:property value="age"/></br>
    <%--获取集合中的数据--%>
    <s:property value="list[0].name"/>
    <s:property value="list[0].age"/></br>
    <s:property value="list[1].name"/>
    <s:property value="list[1].age"/></br>
    <s:property value="list[2].name"/>
    <s:property value="list[2].age"/></br>
    
    • 在值栈Context区域获取数据:需要使用"#"号:
    <%--获取Context中的数据--%>
    <s:property value="#request.username"/></br>
    <s:property value="#session.username"/></br>
    <s:property value="#application.username"/></br>
    <s:property value="#attr.username"/></br>
    

    对应存储数据的Action类:

    public class ValueStackDemo extends ActionSupport {
    
        @Override
        public String execute() throws Exception {
            //获取值栈对象
            //1。通过ActionContext获取valueStack对象
            ValueStack valueStack = ActionContext.getContext().getValueStack();
            //2。通过request获取valueStack对象
            ValueStack valueStack1 = (ValueStack)ServletActionContext.getRequest()
                    .getAttribute(ValueStack.VALUE_STACK);
            //3。存入数据
            User user = new User("张三",19);
            valueStack.push(user);
    
    
            //4.创建集合数据将该数据存放到valueStack中
            List<User> list = new ArrayList<User>();
            list.add(new User("王五",5));
            list.add(new User("费六",6));
            list.add(new User("贵七",7));
            valueStack.set("list",list);
            //5.获取域对象,将数据存储到Context区域
            ServletActionContext.getRequest().setAttribute("username","r区域");
            ServletActionContext.getRequest().getSession().setAttribute("username","s区域");
            ServletActionContext.getServletContext().setAttribute("username","a区域");
    
            return SUCCESS;
        }
    }
    

    2.6 EL表达式在值栈中获取数据:

    EL表达式之所以能从值栈中获取数据,是因为Struts2框架在底层对request.getAttribute()方法进行了增强。


    第三节: OGNL中特殊字符的使用

    3.1 #号:

    • 获取值栈中Context中的数据:
    <%--获取Context中的数据--%>
    <s:property value="#request.username"/></br>
    <s:property value="#session.username"/></br>
    <s:property value="#application.username"/></br>
    
    • 使用#号构建Map集合:
    <%--使用#号构建Map集合--%>
    <s:iterator var="i" value="{'aa','bb','cc','dd'}">
        <s:property value="i"></s:property> --- <s:property value="#i"></s:property>
    </s:iterator>
    

    3.2 %号:

    • 强制解析OGNL
    <%--%的使用--%>
    <%
        request.setAttribute("key","张三");
    %>
    <s:textfield  name="name" value="%{#request.key}"/>
    
    • 强制不解析OGNL(不常用)

    3.3 $号:

    • 在配置文件中使用
      在Struts2的配置文件中使用.xml文件或是属性文件插入变量值是需通过$符合在值栈中获取值。

    相关文章

      网友评论

          本文标题:SSH框架之Struts2进阶OGNL与值栈详解(三)

          本文链接:https://www.haomeiwen.com/subject/lqscwqtx.html