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

版权声明:本文为博主原创文章,转载请注明出处 https://blog.csdn.net/yjy91913/article/details/76039437

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表达式"/>

猜你喜欢

转载自blog.csdn.net/yjy91913/article/details/76039437