Strust2 ognl 和 valueStack详解

OGNL

OGNL简介

OGNL(ObjectGraphic Navigation Language)对象图导航语言,它是一个开源项目。
Struts2框架使用OGNL作为默认的表达式语言,大大加强了数据访问功能。

EL和OGNL的区别

EL表达式不能存放数据,不能调用方法。
OGNL表达式可以存放数据,可以调用方法。

IGNL两大方法

	/**
	 * 根据OGNL表达式进行取值操作
	 * 
	 * @param expression
	 *            ognl表达式
	 * @param ctx
	 *            ognl上下文
	 * @param rootObject
	 *            ognl根对象
	 * @return
	 */
	public static Object getValue(String expression, OgnlContext ctx,
			Object rootObject) {
		try {
			return Ognl.getValue(expression, ctx, rootObject);
		} catch (OgnlException e) {
			throw new RuntimeException(e);
		}
	}

	/**
	 * 根据OGNL表达式进行赋值操作
	 * 
	 * @param expression
	 *            ognl表达式
	 * @param ctx
	 *            ognl上下文
	 * @param rootObject
	 *            ognl根对象
	 * @param value
	 *            值对象
	 */
	public static void setValue(String expression, OgnlContext ctx,
			Object rootObject, Object value) {
		
		try {
			Ognl.setValue(expression, ctx, rootObject, value);
		} catch (OgnlException e) {
			throw new RuntimeException(e);
		}
	}

ognl的核心方法就这两个Ognl.getValueOgnl.setValue已经能够完成对各种对象树的读取和设值工作了。需要特别强调进行区分的,是在针对不同内容进行取值或者设值时,OGNL表达式的不同。

针对根节点
  针对根对象(Root Object)的操作,表达式是自根对象到被访问对象的某个链式操作的字符串表示。

针对非根节点
  针对上下文环境(Context)的操作,表达式是自上下文环境(Context)到被访问对象的某个链式操作的字符串表示,但是必须在这个字符串的前面加上#符号,以表示与访问根对象的区别。

OGNL三要素

我能先开一下案例,然后再去讲解这三要素

	public static void main(String[] args)  {
		//员工对象
		Employee e = new Employee();
		e.setName("小李");
		//管理者对象
		Manager m = new Manager();
		m.setName("张经理");
		
		// 创建OGNL下文,而OGNL上下文实际上就是一个Map对象
		OgnlContext ctx = new OgnlContext();

		// 将员工和经理放到OGNL上下环境中去
		ctx.put("employee", e);
		ctx.put("manager", m);
		// 设置OGNL上下文的根对象
		ctx.setRoot(e);

		/** ********************** 取值操作 *************************** */
		// 表达式name将执行e.getName(),因为e对象是根对象(请注意根对象和非根对象表达式的区别)
		//name没有加#,会直接去根对象中访问看看有没有一个叫做name的属性呀
		String employeeName = (String) OnglExpression.getValue("name", ctx,e);
		//根对象是e,就相对于输出了 e.getName 结果就 小李
		System.out.println(employeeName);

		// 表达式#manager.name将执行m.getName(),注意:如果访问的不是根对象那么必须在前面加上一个名称空间,例如:#manager.name
		String managerName = (String) OnglExpression.getValue("#manager.name",ctx, e);
		//输出结果是 张经理
		System.out.println(managerName);

		//当然根对象也可以使用#employee.name表达式进行访问
		employeeName = (String) OnglExpression.getValue("#employee.name", ctx,
				e);
		//结果是 小李
		System.out.println(employeeName);
		

		/** ********************** 赋值操作 *************************** */
		OnglExpression.setValue("name", ctx, e, "小明");
		employeeName = (String) OnglExpression.getValue("name", ctx, e);
		//这是将根对象(e)的name属性修改为了 小明 自然输出也是小明
		System.out.println(employeeName);
		
		//这是将存入上下文环境中的manager对象的name属性修改成了孙经理
		OnglExpression.setValue("#manager.name", ctx, e, "孙经理");
		managerName = (String) OnglExpression.getValue("#manager.name", ctx, e);
		//输出:孙经理
		System.out.println(managerName);
		
		
		OnglExpression.setValue("#employee.name", ctx, e, "小芳");
		employeeName = (String) OnglExpression.getValue("name", ctx, e);
		//不解释和上面一样,输出小芳
		System.out.println(employeeName);
	}

控制台打印结果

小李
张经理
小李
小明
孙经理
小芳

从上面我们可以推理出OGNL操作的三要素

表单式(Expression)
表达式是整个OGNL的核心,所有的OGNL操作都是针对表达式的解析后进行的。表达式会规定此次OGNL操作到底要干什么。上面程序中getValue("name", ctx,e)代表获取的是根对象的属性。因为没有通过#号指定。getValue("#manager.name",ctx, e);这个表达式就像是指名道姓我要拿谁谁谁的姓名。

根对象 (Root Object)
根对象可以理解为OGNL的操作对象。在表达式规定了“干什么”以后,你还需要指定到底“对谁干”。
  在上面的测试代码中,e就是根对象。这就意味着,我们需要对e这个对象去取name这个属性的值
  
上下文环境 (Context)
  有了表达式和根对象,我们实际上已经可以使用OGNL的基本功能。例如,根据表达式对根对象进行取值或者设值工作。不过实际上,在OGNL的内部,所有的操作都会在一个特定的环境中运行,这个环境就是OGNL的上下文环境(Context)。说得再明白一些,就是这个上下文环境(Context),将规定OGNL的操作“在哪里干”。
  OGNL的上下文环境是一个Map结构,称之为OgnlContext。上面我们提到的根对象(Root Object),事实上也会被加入到上下文环境中去,并且这将作为一个特殊的变量进行处理,具体就表现为针对根对象(Root Object)的存取操作的表达式是不需要增#符号进行区分的。
OgnlContext不仅提供了OGNL的运行环境。在这其中,我们还能设置一些自定义的parameter到Context中,以便我们在进行OGNL操作的时候能够方便的使用这些parameter。不过正如我们上面反复强调的,我们在访问这些parameter时,需要使用#作为前缀才能进行。应为他们不是根对象

Context 上下文对象

OGNL有一个上下文(Context)概念,说白了上下文就是一个Map结构,它实现了java.utils.Map接口,在Struts2中上下文实现为ActionContext。
Struts 2中的OGNL Context实现者为ActionContext,它结构示意图如下:
前台发送请求访问action
  当Struts2接受一个请求时,会迅速创建ActionContext,ValueStack,action。然后把action存放进ValueStack,所以action的实例变量可以被OGNL访问。
  访问上下文(Context)中的非根对象需要使用#符号标注命名空间,如#application、#session,另外OGNL会设定一个根对象(root对象),在Struts2中根对象就是ValueStack(值栈) 。如果要访问根对象(即ValueStack)中对象的属性,则可以省略#命名空间,直接访问该对象的属性即可。

值栈 ValueStack

  valueStack是struts2的值栈空间,是struts2存储数据的空间
  Strut2的Action类通过属性可以获得所有请求相关的值,要在Action类中声明与参数同名的属性或者以类名小写加属性名如:user.name,就可以获得这些参数值,在Struts2调用Action类的Action方法(默认是execute方法)之前,就会为相应的Action属性赋值。这项功能Struts2要依赖于ValueStack对象来实现,每个Action类的对象实例会拥有一个ValueStack对象,这个对象贯穿整个Action的生命周期,
  当Struts2接收到一个请求后,会先建立Action类的对象实例,但并不会立即调用Action方法,而是先将Action类的相应属性放到ValueStack实现类ONGLValueStack对象root对象的顶层节点。只是所有的属性值都是默认的值,如String类型的属性值为null,int类型的属性值为0等。在处理完上述工作后,Struts 2就会调用拦截器链中的拦截器,这些拦截器会根据用户请求参数值去更新ValueStack对象顶层节点的相应属性的值,最后会传到Action对象,并将ValueStack对象中的属性值,赋给Action类的相应属性。当调用完所有的拦截器后,才会调用Action类的Action方法。ValueStack会在请求开始时被创建,请求结束时消亡。

值栈的数据结构

在这里插入图片描述
值栈分为两个部分:root和context
root
root是List类型并且是一个栈的结构实现的,就是使用他存放一组对象 ,在root变量中处于第一位的对象叫栈顶对象。通常我们在OGNL表达式里直接写上属性的名称即可访问root变量里对象的属性,搜索顺序是从栈顶对象开始寻找,如果栈顶对象不存在该属性,就会从第二个对象寻找,如果没有找到就从第三个对象寻找,依次往下访问,直到找到为止。
由于ValueStack(值栈)是Struts 2中OGNL的根对象,如果用户需要访问值栈中的对象,在JSP页面可以直接通过下面的EL表达式访问ValueStack(值栈)中对象的属性: ${foo} //获得值栈中某个对象的foo属性。如果访问其他Context中的对象,由于他们不是根对象,所以在访问时,需要添加#前缀

context
ValueStack中的Context域与ActionContext的Context域是相关联域。
ActionContext中存储了值栈的内存地址,可以理解为ValueStack的Context与ActionContext是同一个值。

值栈findValue方法获取值的套路

我们使用下面的案例来证明它时应该栈


	/**
	 * 此例用于模拟struts2的值栈计算过程
	 * 
	 * @param args
	 */
	public String test1() {
		//valueStack是一个堆栈的容器, 特点:前进后出
		ValueStack vs = ServletActionContext.getContext().getValueStack();
		vs.push(new Employee("张雇员", 2000));// 1
		vs.push(new Student("小明同学", "s001"));// 0
		//栈的自定向下找字段,最近的获取并输出
		System.out.println(vs.findValue("name"));
		System.out.println(vs.findValue("salary"));
		return "rs";
	}

结果是

小明同学
2000

原因:栈是应该先进后出的结构,上面代码中Employee先入展,Student后入栈。所有Student在Employee的上面(也就是压着Employee)。
vs.findValue("name")
当我们获取name这个属性的时候,它会先去Student找找看看有没有name这个属性呀。找到了就返回。而Employee也有name属性,但是呢它在Student下面。所以不会被访问到。
vs.findValue("salary")
当我们获取salary这个属性的值,同样的带来,会先去Student里面找,由于Student没有定义salary这个属性,所以就去Employee里面找,Employee中定义了salary属性,值为2000.那就输出2000

valueStack的赋值套路

下面我们将通过一个代码案例来帮助你更好的理解值栈。
定义一个Action类

public class TestAction implements ModelDriven<Cal>{
	
	private Cal cal;
	private String num;
	
	public String test() {
		// TODO Auto-generated method stub
		//cal使用的是ModelDriven赋值
		System.out.println("cal:" + cal);
		//num使用的是get/set赋值
		System.out.println("num:" + num);
		return "rs";
	}

	@Override
	public Cal getModel() {
		// TODO Auto-generated method stub
		return cal;
	}
}

Cal 代码

public class Cal{
	private String num;
	public String getNum() {
		return num;
	}
	public void setNum(String num) {
		this.num = num;
	}
	@Override
	public String toString() {
		return "Cal [num=" + num + "]";
	}
}

注:这里就不展示为TestAction配置xml了
下面我们再前台访问TestAction的test方法并将参数num传过去。访问路径如下
/sy/testAction.action?num1=20
结果是 cal.num为20,全局变量num为null,并不是我们猜想的num也为20(公开了set/get方法)

原因当值栈通过前台参数给后台的TestAction的全局参数赋值时。值栈中cal这个变量在num这个变量的前,也就是更靠近栈顶。当赋值时会最先给cal赋值。就这样num的值赋进了cal的num属性中,前台参数已经被赋过了,所有后面的num就不会被赋值,自然就为null

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
蛋白质是生物体中普遍存在的一类重要生物大分子,由天然氨基酸通过肽键连接而成。它具有复杂的分子结构和特定的生物功能,是表达生物遗传性状的一类主要物质。 蛋白质的结构可分为四级:一级结构是组成蛋白质多肽链的线性氨基酸序列;二级结构是依靠不同氨基酸之间的C=O和N-H基团间的氢键形成的稳定结构,主要为α螺旋和β折叠;三级结构是通过多个二级结构元素在三维空间的排列所形成的一个蛋白质分子的三维结构;四级结构用于描述由不同多肽链(亚基)间相互作用形成具有功能的蛋白质复合物分子。 蛋白质在生物体内具有多种功能,包括提供能量、维持电解质平衡、信息交流、构成人的身体以及免疫等。例如,蛋白质分解可以为人体提供能量,每克蛋白质能产生4千卡的热能;血液里的蛋白质能帮助维持体内的酸碱平衡和血液的渗透压;蛋白质是组成人体器官组织的重要物质,可以修复受损的器官功能,以及维持细胞的生长和更新;蛋白质也是构成多种生理活性的物质,如免疫球蛋白,具有维持机体正常免疫功能的作用。 蛋白质的合成是指生物按照从脱氧核糖核酸(DNA)转录得到的信使核糖核酸(mRNA)上的遗传信息合成蛋白质的过程。这个过程包括氨基酸的活化、多肽链合成的起始、肽链的延长、肽链的终止和释放以及蛋白质合成后的加工修饰等步骤。 蛋白质降解是指食物中的蛋白质经过蛋白质降解酶的作用降解为多肽和氨基酸然后被人体吸收的过程。这个过程在细胞的生理活动中发挥着极其重要的作用,例如将蛋白质降解后成为小分子的氨基酸,并被循环利用;处理错误折叠的蛋白质以及多余组分,使之降解,以防机体产生错误应答。 总的来说,蛋白质是生物体内不可或缺的一类重要物质,对于维持生物体的正常生理功能具有至关重要的作用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值