struts2 之ognl表达式与值栈(03)

来源:互联网 发布:石家庄手机数据恢复 编辑:程序博客网 时间:2024/05/16 09:05

ognl:

概述

是一种功能强大的表达式语言
struts2把它作为了默认的表达式语言(导入jar包)

作用:

(理解)可以调用对象的方法
可以访问类的静态属性
可以访问类的静态方法
★获取值栈中的数据
例如

调用对象的方法:

<s:property value="'hello'.length()"/><br>

访问类的静态属性:

<!-- @全限定名@属性名称 --><s:property value="@java.lang.Math@PI"/><br>

访问类的静态方法:

<!-- @全限定名@方法名称() 前提:允许ognl访问类的静态方法--><s:property value="@java.lang.Math@random()"/>

获取值栈的数据

这个在后面讲

值栈:

ValueStack:接口 我们使用的是他的实现类:OgnlValueStack
ValueStack实际上就是一个容器。它由Struts框架创建,当前端页面如jsp发送一个请求时,
Struts的默认拦截器会将请求中的数据进行封装,并入ValueStack的栈顶
我把他称之为struts的临时数据中转站.当请求来的时候,struts框架会为每一次请求创建一个valuestack,
把请求中的数据(请求参数,域中的数据)获取,放入值栈.我们若向获取请求的参数可以找request拿,也可以找ValueStack拿;
我们若向往域中放入数据,可以先找到相应的域对象,然后操作;也可以通过值栈操作域中的数据.
当响应生成的时候,值栈就销毁了.


值栈的组成部分:

值栈的内部结构
主要有两个东西:
root:本身是一个list

context:本身是一个map key:string value:Object
主要存放的是:

key value request request域中的数据(map) session session域中的数据(map) application application域中的数据(map) attr 四个域中的数据(map) parameters 请求的参数(map)

这个图是ValueStack和ActionContext的关系图

ActionContext和ValueStack的关系

我们可以把ActionContext看成是一个工具类

请求来的时候,struts会在核心过滤器中为每一次请求创建一个ActionContext,也会创建一个ValueStack
下面是核心过滤器的doFilter的代码

 public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {        HttpServletRequest request = (HttpServletRequest) req;        HttpServletResponse response = (HttpServletResponse) res;        try {            prepare.setEncodingAndLocale(request, response);//设置编码格式,防止乱码            prepare.createActionContext(request, response);//创建ActionContext把request都传入当参数            prepare.assignDispatcherToThread();            if ( excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) {                chain.doFilter(request, response);//放行不拦截的资源            } else {                request = prepare.wrapRequest(request);                ActionMapping mapping = prepare.findActionMapping(request, response, true);                if (mapping == null) {                    boolean handled = execute.executeStaticResourceRequest(request, response);                    if (!handled) {                        chain.doFilter(request, response);                    }                } else {                    execute.executeAction(request, response, mapping);                }            }        } finally {            prepare.cleanupRequest(request);        }    }

我们可以继续看一下createActionContext()方法

 public ActionContext createActionContext(HttpServletRequest request, HttpServletResponse response) {        ActionContext ctx;//先声明        Integer counter = 1;        Integer oldCounter = (Integer) request.getAttribute(CLEANUP_RECURSION_COUNTER);        if (oldCounter != null) {            counter = oldCounter + 1;        }        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 {            //创建值栈            ValueStack stack = dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack();            //dispatcher.createContextMap()方法是把所有的数据都放入,把request的参数,已经request获取域中数据都放入context中,request对象,response对象            //然后在把这个Context放入stack对象中            stack.getContext().putAll(dispatcher.createContextMap(request, response, null, servletContext));            把这个这个stack对象通过context的构造器,传入,再创建对象            ctx = new ActionContext(stack.getContext());        }        request.setAttribute(CLEANUP_RECURSION_COUNTER, counter);        //下面这句话是绑定当前线程(ThreadLocal中)        ActionContext.setContext(ctx);        return ctx;    }

将请求的参数和域中的数据放入ValueStack中.然后将ValueStack放入ActionContext中.

ActionContext会绑定到当前线程.方便我们在自己的action中获取到所有的数据.

这个是我做个了简单的流程图
简单流程图

获取值栈:

获取值栈:ActionContext.getContext().getValueStack();

往值栈中存数据:

方式一:通过 push对象 set集合

★往root中放数据:
方式1:值栈的api
push(Object obj)
set(String key,Object value):底层创建了一个hashmap,将key/value作为map的键值对,然后将map放入root中

//获取值栈        ValueStack vs = ActionContext.getContext().getValueStack();        //操作        User user = new User();        user.setAge(18);        user.setUsername("三三");        vs.push(user);        //在user上面 底层是一个map集合,然后把map放入root中        vs.set("name", "思思");

方式2:通过Action的成员属性

(理解)往context中放数据:
我们看源码的时候,看到struts将值栈的context放入到了ActionContext中,我们只需要操作ActionContext就可以值栈的Context了
put(key,value) 相当于往request域中放入数据
getSession().put(key,value) 放入Session中
getApplication.put(key,value) 放入Application
例如:

    private String hobby;    public String getHobby() {        return hobby;    }    public void setHobby(String hobby) {        this.hobby = hobby;    }    @Override    public String execute() throws Exception {        hobby="抽烟喝酒烫头";        return super.execute();    }

在jsp获取值栈

★el也可以获取值栈中所有的数据

之前使用el 例如:${username}
依次从pageContext,request,session,application中查找数据,找到之后立即返回,找不到返回””
底层调用的是: pageContext.findAttribute(“username”)
先去找pageContext域中的数据
若找到立即返回
若找不到继续往下一个域中找

不在struts中ei标签的查找顺序:

找request域中
若找到立即返回
若找不到继续往下一个域中找

找session域中
若找到立即返回
若找不到继续往下一个域中找

找application域中
若找到立即返回
若找不到返回null

在struts中el 查找顺序:

pageContext – request – root – context(大map) – session – application
在核心过滤器中增强了request.getAttribute(..) 找的范围:request – root – context(大map)
底层调用了值栈的findValue方法:先查root,root中没有的话继续找context(大map)

总结:
从值栈中取数据:
获取root中的数据,ognl表达式直接写属性名称即可

<s:property value="ognl表达式"/>

扩展:先找root中的数据,找不到继续查找context(大map)中的数据,找不到返回””
获取context中数据,ognl表达式需要加”#”

<s:property value="#ognl表达式"/>