美文网首页程序员
Struts2_ognl表达式&操作值栈

Struts2_ognl表达式&操作值栈

作者: 明天你好向前奔跑 | 来源:发表于2017-07-10 00:00 被阅读0次

    Struts2_OGNL表达式

    一、初步使用ongl表达式

    OGNL是Object Graphic Navigation Language(对象图导航语言)的缩写,是一个使用简单、功能强大的、开源的表达式语言(框架),可以方便地操作任何的对象属性、方法等。

    Struts2框架使用OGNL作为默认的表达式语言,主要用于页面的取值。它类似于EL表达式语言,但比EL语法强大很多。

    EL Expression Language 表达式语言, 主要用来获取 JSP页面四个域范围数据 (page、 request、 session、 application )

    使用ongl表达式

    1. 导入的jar包:
        ognl-xxx.jar : ognl的核心环境
        javassist-xxx.jar : ognl的依赖,提供字节码修改能力。
    
    2. 使用ognl表达式的jsp页面需要导入struts2的标签库
        <%@ taglib prefix="s" uri="/struts-tags"%>
    
    3. 几种基本的使用方式:
        1. Java对象的直接访问 :
            <s:property value="'name'"/><br/>
        
        2. 实例方法调用
            <s:property value="'name'.length()"/><br/>      
    
        3. 赋值操作或表达式串联
            <s:property value="1+1" /><br/>
    
        4. 静态方法调用(类的静态方法或静态变量的访问) 不推荐
            语法:@[类全名(包括包路径)]@[方法名|值名]
            例如:<s:property value="@java.lang.Math@max(10,20)"/><br/>
    
            但是!!!在使用静态方法之前必须在struts.xml中开启静态方法调用功能
            <!-- 开启ognl表达式静态方法调用 -->
            <constant name="struts.ognl.allowStaticMethodAccess" value="true"></constant>
    
    
        5. OGNL上下文(OGNL Context)和ActionContext的访问。(值栈相关访问-重要)
        6. 集合对象的操作
    

    二、值栈

    值栈的概念

    值栈(ValueStack),是Struts2的数据中转站,其中自动保存了当前Action对象和其他相关对象(包括常用的Web对象的引用,如request等),也可以手动保存自己的数据对象,同时也可以随时随地将对象从值栈取出或操作(通过OGNL表达式)

    了解:值栈的生命周期,和request一样或action,生命周期都一样

    获取值栈对象

    在Action动作类中获取值栈对象有两种方式:

    @Override
    public String execute() throws Exception {
    
        //目标 :获取值栈对象
        //方式一 : 通过request对象直接获取
        ValueStack valueStack = (ValueStack) ServletActionContext.getRequest().getAttribute("struts.valueStack");
        
        //方式二 : 通过struts2提供的api来获取(底层还是1方式)
        ValueStack valueStack2 = ActionContext.getContext().getValueStack();
        
        //测试是否是同一个对象
        System.out.println(valueStack == valueStack2);
        return NONE;
    }
    

    值栈的数据存储结构

    在值栈的内部有两个逻辑部分:

    • ObjectStack(对象栈):保存了Action的相关对象和动作,数据存储结构是List。
    • ContextMap(上下文栈):保存了各种映射关系,如常用的web对象的引用,数据存储结构是Map。
    
        值栈ValueStack(OgnlValueStack实现类)中有两块区域 :
        一块是CompoundRoot对象栈(也称为Root栈),它实质上是使用List集合来存放对象,存入对象压栈从栈顶往下压。
    
        一块是OgnlContext上下文栈(也称为Map栈)。
            Map栈中有两个区域,_root用于存放对象栈(Root栈)的引用
            _values实质上使用Map集合键值对的形式存放对象
    
    img12.png

    值栈是在请求对象引用了一块存储空间。

    值栈包括两部分: 对象栈(CompoundRoot,继承List接口)和OGNL上下文栈(OgnlContext,实现Map接口)
    而OGNL上下文栈内部又分为两部分:对象栈的引用和一个HashMap,这个HashMap会引用常用web对象和其他映射关系。

    三、 操作值栈

    栈的概念:
    栈是一种数据结构,它按照先进后出的原则存储数据,即先进入的数据被压入栈底,
    最后进入的数据在栈顶,需要读取数据的时候,从栈顶开始弹出数据(即最后一个数据被第一个读出来)。
    
    栈也被成为先进后出表,可进行插入和删除操作,插入称之为进栈(压栈)(push),
    删除称之为退栈(pop),允许操作的两端分别称为栈顶(top)和栈底(bottom)。栈底固定,而栈顶浮动。
    
    对于栈就只能每次访问它的栈顶元素,从而可以达到保护栈顶元素以下的其他元素的目的。
    

    值栈数据的保存:向root栈和map栈放入数据

    @Override
    public String execute() throws Exception {
    
        // 目标 :向root栈和map栈中分别存入数据
        String msg = "我爱java";
        
        // 获取值栈对象
        ValueStack valueStack = ActionContext.getContext().getValueStack();
        
        //向root栈中存值(压栈),匿名,
        valueStack.push(msg);
        //root栈中存值(压栈),有名字
        valueStack.set("msg2", "我爱C++");
        //向Root栈中存值(键值对)
        ActionContext.getContext().put("msg3", "我爱Python");
    
        return SUCCESS;
    }
    

    通过ognl表达式取出存储在值栈中的数据显示在页面

    struts.xml 配置跳转页面 :
        <action name="valueStack2" class="com.itdream.a_valueStack.ValueStack2Action">
                <result>/a_ognl/result.jsp</result>
        </action>
    
    
    result.jsp : 
    
    <%@ taglib prefix="s" uri="/struts-tags"%>
    
    <!-- 取出存在值栈中的值 -->
    <!-- push方法,匿名压入Root栈的数据,第一个被压入 -->
    <s:property value="[1].top"/><br/>
    
    <!-- set方法,Struts2帮我们创建一个map集合存储数据压入Root栈的数据,map的key就是数据的名字,通过key取值 -->
    <s:property value="msg2"/><br/>
    
    <!-- put方法,放入Map栈中,通过加#号定向到Map栈中找 -->
    <!-- 也可以不加#号使用默认查找机制,但这样先从Root栈先找,再到Map栈找key,如果在Root栈找到就找不到Map栈的数据了 -->
    <s:property value="#msg3"/>|<s:property value="msg3"/><br/>
    
    <!-- 用来查看值栈的内容 -->
    <s:debug/>
    
    img14.png

    点击Debug查看值栈的内容:

    页面中直接查看值栈的元素的方法。
    struts2为我们提供了一个标签:<s :debug/>,只要将其加在页面中即可。
    

    Root栈:

    数据从栈顶往下压,push方法直接压入String对象,此时它是栈顶,索引为0
    然后使用set方法,创建了一个HashMap存储数据的值,作为对象存入Root栈,此时栈顶是这个HashMap对象,String对象的索引变为1
    
    img15.png

    Map栈:

    put方法,以键值对形式存储数据
    
    img16.png

    在这里介绍值栈的创建过程:

    1. 每次请求都会创建一个ActionContext对象和一个OnglValueStack值栈对象,这时在Root栈中放入了一个textProvider.
    
    2. 紧接着,Struts2将与Servket有关的Response,Request,Session等对象放入到Map栈中,此时Map栈中有Servlet的对象引用.
    
    3. 之后就准备执行Action动作类 ,创建Action动作的代理对象。Action是在执行的时候才进行初始化,初始化完成后,
    将Action对象压入Root栈栈顶,并在Map栈的_root中放入一个引用:key - action
    
    到这里,Root栈中有一个textProvider与一个Action对象,Map栈中有Servlet相关的数据以及Action对象的引用,
    两个Action是同一个
    
    4. 初始化完成,ActionProxy代理对象在执行时,会先执行拦截器,再执行Action,
    这个时候,如果有Action实现了ModelDriven接口,模型驱动拦截器就将模型对象压入栈顶。
    
    
    了解原理的目的:
    
    知道值栈的初始化时机,访问action的时候,才创建(这个过程中会创建actionProxy对象,并且同时创建值栈,并且,将一些对象放入值栈。)
    
    值栈初始化之后,里面主要默认有:root栈(textProvider、action类对象、model(user)),map栈(servlet相关对象、action对象一个引用)
    
    哪些对象在栈顶!因为后面我们从值栈取值,都从栈顶往下取。所以,一定要知道哪个对象在栈上面。
    
    img13.png

    值栈的默认搜索机制

    测试代码:

        
        public class ValueStack3Action extends ActionSupport {
            
            // 目标 : 测试值栈的默认查找机制
            // 提供Action的属性,Action初始化完成时,会将Action的对象属性压入Root栈
            private String username = "林志玲";
        
            public String getUsername() {
                return username;
            }
        
            public void setUsername(String username) {
                this.username = username;
            }
        
            @Override
            public String execute() throws Exception {
        
                ValueStack valueStack = ActionContext.getContext().getValueStack();
                //在创建一个有名字的对象压入Root栈
                valueStack.set("username", "唐嫣");
                
                //往Map栈存入数据
                ActionContext.getContext().put("username", "高圆圆");
                
                return SUCCESS;
            }
        }
    
    
        jsp页面:
    
        <!-- 取出存在值栈中的值 -->
        <s:property value="username"/>
        
        <!-- 用来查看值栈的内容 -->
        <s:debug/>
    
    
        此时运行取出username的值是 唐嫣 。
    
        为什么呢?这是由于值栈的默认查找机制。
        当我们使用 对象名称 直接查找时,底层使用的API是valuestack.find(ognl)
        1. 他会优先从Root栈中开始查找(从上而下),因为Action属性存入值栈的是在执行前压入的,因此取的时候,它在值栈的下方,
        因此首先找到上面的执行时 set 方法放入的 username。
    
        2. 如果Root栈找不到,才会找Map栈中的key.如果想直接获取Map栈中的数据,加个#号即可
            <s:property value="#username"/>
        
    

    OGNL操作值栈的几个符号:

    直接使用对象名 , # , % , $
    
    1. 直接使用对象名,使用值栈的默认查找机制
    
    2. #+key 从Map栈中查找对象的key,从而找到该数据
        1. 通过#request等可以获取存储在Servlet域对象中的数据
    
    3. % , 控制解析或者不解析
    
        添加%{'对象名'}或者'对象名'让其变成普通字符串而不解析 。
        <!-- %强制不转换 -->
        <s:property value="username"/> <!-- 唐嫣 -->
        <s:property value="'username'"/><!-- username -->
        <s:property value="%{'username'}"/><!-- username -->
    
        使用%{对象名}强制解析
        <!-- 这里s:textfield标签类似与input的text标签 -->
        <s:textfield value="username"/>
        <!-- textfield标签的value中直接写对象名称不会进行解析,需要%强制解析 -->
        <s:textfield value="%{username}"/>
    
    4. $用于在配置文件中获取值栈的值
        
        1.URL请求重定向时,携带(动态传递)参数 –struts.xml中使用
            result type="redirect">/c_expr/ognl2.jsp?name=${myname}</result>
    

    值栈的存取汇总小结

    值栈的主要作用就是数据的保存和获取(可以在任何地方获取)。

    1.如何向值栈保存数据
    • ValueStack.put(obj):保存数据到Root栈(栈顶),(对象本身)匿名,通过索引取出
    • ValueStack.set(key,value):保存数据到Root栈(栈顶),数据被自动封装成一个Map集合存储,栈顶保存的是一个Map集合,Map的value就是对象,这种存储方式有名字.ognl通过名称获取数据的值
    • ActionContext.getContext().put(key,value): 保存数据到Map栈中
    • 提供Action成员变量,提供getter方法(Action就在root栈中,Action属性可以被搜索)

    模型驱动封装参数优先于属性驱动的原理:

    
        第四种方式在初始化完成时压入Root栈,而ModelDriven过滤器待封装的参数,在初始化完成后,过滤器中,将其压入栈顶。
        而封装的参数从Root栈中取值是自上而下取值的,因此模型驱动优先于属性驱动。
    
    2. ognl表达式如何获取值栈的数据
    • JSP页面获取
      • <s :property value= “对象名”/> 先搜索root栈对象属性(getter方法: getXxx-->xxx),再搜索map的key
      • <s :property value= “#对象名”/>搜索map栈的key
      • 通过[index].top指定访问Root栈的某个对象,例如[0].top访问栈顶对象
    • Action代码获取
      • ValueStack.findValue(ognl表达式) ; 获取值栈数据
    3. 值栈的生命周期
    * 贯穿整个Action的生命周期,每个Action类的对象实例都拥有一个ValueStack对象。
    * Struts2框架将ValueStack对象保存在名为“struts.valueStack”的请求(request)属性中,
    即值栈是request中的一个对象,一个请求对应一个Action实例和一个值栈对象。
    
    因此:值栈的生命周期,就是request生命周期。值栈、action、request生命周期一样,不存在线程问题!
    重定向跳转页面时,目标页面无法获取到Action中放入值栈的值。
    
    4. 补充,通过EL表达式获取值栈的值
    EL 表达式原理:
     在page、request、session、application 四个范围,调用getAttribute 获取数据。为什么也可以获取值栈的值呢?
    
    原因:Struts2提供StrutsRequestWrapper包装类,对request 的 getAttribute 方法增强 。
    它优先使用 request.getAttribute取值,如果取不到,执行 valueStack的findValue方法使用值栈的默认查找机制。
    
    问题思考 : 
    后台代码:
    request.setAttribute(“name“, ”aaa“ ) ;//存入request域中
    valueStack.set(“name“,”bbb“ )//存入值栈
    页面代码:
    <s:property name=”name” />   -----  值栈默认查找机制,bbb
    ${name} ------ 优先从request中查找,aaa 
    

    四、CRM案例:添加,显示,修改客户

    1. 搭建环境

    1.1 搭建Struts2环境
    • 拷贝前端页面
    • 导入Struts2的jar包
    • 配置web.xml的前端控制器
    • 配置struts.xml文件
    • Struts2环境搭建完成
    1.2 搭建Hibernate环境
    • 导入Hibernate的jar包

      • required包下的所有jar包
      • c3p0的jar包
      • 另,log4j-1.x的jar包
    • 删除Struts2与Hibernate冲突的jar包

    • R : 数据库表的编写

    • O : 持久化类的编写

    • M : 映射文件的编写

    • 导入日志log4l.properties配置文件,创建核心配置文件hibernate.cfg.xml

    • 准备工具类HibernateUtils

    • 创建包结构,测试Hibernate是否搭建成功

    • Hibernate搭建完成

    2. 代码实现功能

    2.1 修改menu.jsp
    新增客户 : 跳转到增加新客户的页面
        href="${pageContext.request.contextPath }/jsp/customer/add.jsp"
    
    客户列表 : 查询所有客户
        href="${pageContext.request.contextPath }/customer_list.action"
    
    2.2 修改add.jsp
    form 表单提交路径 : 
        action="${pageContext.request.contextPath }/customer_save.action"
    
    2.3 struts.xml请求与Action的配置关系
    <struts>
        <constant name="struts.devMode" value="true" />
        <!-- 简单样式 -->
        <constant name="struts.ui.theme" value="simple"/>
    
        <package name="default" namespace="/" extends="struts-default">
            <action name="customer_*" class="com.itdream.crm.web.action.CustomerAction" method="{1}">
                <result name="flushListCustomer" type="redirectAction">customer_list.action</result>
                <result name="listCustomer">/jsp/customer/list.jsp</result>
                <!-- 默认转发,传递customer数据 -->
                <result name="editjsp">/jsp/customer/edit.jsp</result>
            </action>
        </package>
    </struts>
    
    2.4 Action动作类处理请求
    1. 提供模型驱动,用以封装参数。
    2. 查询到的数据,存入值栈转发。
    3. 下面是三个功能:新增客户,查询所有客户,修改客户
    
    public class CustomerAction extends ActionSupport implements ModelDriven<Customer>{
    
        //提供模型对象
        private Customer customer = new Customer();
        
        @Override
        //Struts2获取模型对象封装
        public Customer getModel() {
            return customer;
        }
        
        //添加客户
        public String save() {
            //获取参数,模型驱动自动封装
            //调用业务层
            CustomerService service = new CustomerServiceImpl();
            service.addCustomer(customer);
            
            return "flushListCustomer";
        }
        
        //查询所有客户
        public String list() {
            //调用业务层
            CustomerService service = new CustomerServiceImpl();
            List<Customer> listCustomer = service.findListCustomer();
            
            //将所有Customer的数据存入值栈,以set方式存入Root栈
            ValueStack valueStack = ActionContext.getContext().getValueStack();
            valueStack.set("list", listCustomer);;
            //跳转页面
            return "listCustomer";
        }
        
        //查询要修改的客户,并跳转到修改页面
        public String showEdit() {
            //获取参数
            Long cust_id = customer.getCust_id();
            //调用Service层
            CustomerService service = new CustomerServiceImpl();
            Customer customer2 = service.findCustomerById(cust_id);
            //将查询到的客户数据压入值栈
            ActionContext.getContext().getValueStack().set("customer", customer2);
            //跳转页面
            return "editjsp";
        }
    
        //修改客户信息
        public String update() {
            //获取参数(模型驱动)
            //调用业务层
            System.out.println("hehe");
            CustomerService service = new CustomerServiceImpl();
             service.updateCustomer(customer);
            //跳转
            return "flushListCustomer";
        }
    }
    
    2.5 Service层处理业务逻辑
    public class CustomerServiceImpl implements CustomerService {
    
        @Override
        // 添加客户
        public void addCustomer(Customer customer) {
            // 获取Session对象
            Session session = HibernateUtils.getCurrentSession();
            // 开启事务
            Transaction transaction = session.beginTransaction();
            // 业务逻辑
            try {
                CustomerDAO dao = new CustomerDAOImpl();
                dao.addCustomer(customer);
            } catch (Exception e) {
                // struts2默认异常回滚事务,可以不写
                transaction.rollback();
                e.printStackTrace();
            } finally {
                // 无论是否异常都提交事务结束事务
                transaction.commit();
            }
        }
    
        @Override
        // 查询所有客户
        public List<Customer> findListCustomer() {
            // 获取Session对象
            Session session = HibernateUtils.getCurrentSession();
            // 开启事务
            Transaction transaction = session.beginTransaction();
            // 业务逻辑
            List<Customer> listCustomer = null;
            try {
                CustomerDAO dao = new CustomerDAOImpl();
                listCustomer = dao.findListCustomer();
            } catch (Exception e) {
                // struts2默认异常回滚事务,可以不写
                transaction.rollback();
                e.printStackTrace();
            } finally {
                // 无论是否异常都提交事务结束事务
                transaction.commit();
            }
            return listCustomer;
        }
    
        @Override
        // 通过id查找客户
        public Customer findCustomerById(Long cust_id) {
            // 获取Session对象
            Session session = HibernateUtils.getCurrentSession();
            // 开启事务
            Transaction transaction = session.beginTransaction();
    
            // 业务逻辑
            Customer customer = null;
            try {
                CustomerDAO dao = new CustomerDAOImpl();
                customer = dao.findCustomerById(cust_id);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                transaction.commit();
            }
            return customer;
        }
    
        @Override
        // 更新客户信息
        public void updateCustomer(Customer customer) {
            // 获取Session对象
            Session session = HibernateUtils.getCurrentSession();
            // 开启事务
            Transaction transaction = session.beginTransaction();
            // 业务逻辑
            try {
                CustomerDAO dao = new CustomerDAOImpl();
                dao.updateCustomer(customer);
            } catch (Exception e) {
                // Struts2默认异常回滚
                transaction.rollback();
                e.printStackTrace();
            } finally {
                // 提交事务
                transaction.commit();
            }
        }
    }
    
    2.6 dao层操作数据库
    1. 添加客户需要先找到其关联的联系人建立关系
    2. 持久态的使用
    
    public class CustomerDAOImpl implements CustomerDAO {
    
        @Override
        //添加客户
        public void addCustomer(Customer customer) {
            //获取Session对象
            Session session = HibernateUtils.getCurrentSession();
            //首先找到客户的联系人
            //创建Criteria查询对象
            Criteria criteria = session.createCriteria(Linkman.class);
            //创建限制条件
            Criterion criterion = Restrictions.eq("lkm_name", customer.getCust_linkman());
            //添加限制条件
            criteria.add(criterion);
            //查询联系人
            Linkman linkman = (Linkman) criteria.uniqueResult();
            if(linkman == null) {
                linkman = new Linkman();
                linkman.setLkm_name(customer.getCust_linkman());
            }
            
            //保存客户到数据库
            //建立关系:客户表达与联系人的关系:一个客户有多个联系人
            customer.getLinkmans().add(linkman);
            //建立关系:联系人表达与客户的关系:一个联系人对应一个客户
            linkman.setCustomer(customer);
            
            //将customer变为持久态,配置了级联,会自动级联将linkman也变为持久态
            session.save(customer);
        }
    
        @Override
        //查询所有客户
        public List<Customer> findListCustomer() {
            //单表查询使用Criteria查询更方便
            //获取Session对象
            Session session = HibernateUtils.getCurrentSession();
            //创建Criteria查询对象
            Criteria criteria = session.createCriteria(Customer.class);
            //执行查询
            return criteria.list();
        }
    
        @Override
        //通过ID查询客户
        public Customer findCustomerById(Long cust_id) {
            //获取Session
            Session session = HibernateUtils.getCurrentSession();
            //get方法获取的Customer已经是持久态无需保存
            return session.get(Customer.class, cust_id);
        }
    
        @Override
        //更新客户信息
        public void updateCustomer(Customer customer) {
            //获取Session对象
            Session session = HibernateUtils.getCurrentSession();
            //修改客户
            session.update(customer);
        }
    }
    
    2.7 显示所有客户list.jsp获取值栈数据
    <!-- 神奇的request先从request中找,找不到就走值栈的默认查找机制,因此也能查找到存入值栈的list -->
    <c:forEach items="${list }" var="customer">
        <TR style="FONT-WEIGHT: normal; FONT-STYLE: normal; BACKGROUND-COLOR: white; TEXT-DECORATION: none">
        <TD>${customer.cust_name }</TD>
        <TD>${customer.cust_level }</TD>
        <TD>${customer.cust_source }</TD>
        <TD>${customer.cust_linkman }</TD>
        <TD>${customer.cust_phone }</TD>
        <TD>${customer.cust_mobile }</TD>
        <TD>
        <a href="${pageContext.request.contextPath }/customer_showEdit.action?cust_id=${customer.cust_id}">修改</a>
        </TD>
    
    2.8 edit.jsp获取存入值栈的客户信息,提交修改
    form表单action请求地址:
        action="${pageContext.request.contextPath }/customer_update.action"
    
    //隐藏域提交客户id,从而找到该客户进行数据更新
    <s:textfield name="cust_id" value="%{customer.cust_id}" type="hidden" />
    
    客户名称:<s:textfield name="cust_name" value="%{customer.cust_name}" />
    客户级别:<s:textfield name="cust_level" value="%{customer.cust_level}" />
    信息来源:<s:textfield name="cust_source" value="%{customer.cust_source}" />
    联系人:<s:textfield name="cust_linkman" value="%{customer.cust_linkman}" />
    固定电话 :<s:textfield name="cust_phone" value="%{customer.cust_phone}" />
    移动电话 :<s:textfield name="cust_mobile" value="%{customer.cust_mobile}" />
    
    2.9 功能完成,测试

    结果图:

    img17.png img18.png img19.png

    相关文章

      网友评论

        本文标题:Struts2_ognl表达式&操作值栈

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