OGNL

一:OGNL简介

OGNL的全称是Object  Graph  Navigation  Language即对象导航语音。它是一个开源项目,工作在视图层,用来取代页面中的java脚本。简化数据库的访问操作。

OGNL是一种强大的技术,被集成在Struts 2框架中用来帮助实现数据的传输和类型转换。

表达式语音:在视图层将数据从页面传入框架和从框架获取输出数据生成页面的过程,OGNL提供了一个简单的语法,将表单与Struts 2标签与java各种类型的数据绑定起来,列如:

<input  type=”text”  name=”user.name”/>的输入对应Action类中User对象的name属性。

类型转换器:每次数据流入和流出框架,页面中的字符串类型的数据和java数据类型之间都会发生转换。

 

 

一、OGNL全称是Object Graph Navigation Language,即对象导航图语言

 OGNL在框架中主要做两件事情:表达式语言和类型转换器

二:OGNL在框架中的作用以及数据的流入流出:

 

 






 

二、ValueStack值栈

定义:是内存中的一块空间,栈和堆之外的空间,它具有栈的特征,可以存放多个对象,如果存放多个对象,他们是按照先后顺序压入堆栈的。框架在处理每个请求时,都会创建该请求对应的运行环境,这时会创建值栈和请求对应的Action实例,并将Action实例压入值栈中  






 三、实现思路

可通过单列(Root)、双列(Context)集合获取ValueStack

单列集合:

 

创建Action类继承自ActionSupport,首先通过ServletActionContext获取到request对象,其次创建UserInfo对象,通过ValueStack对象的getRoot方法拿到UserInfo对象info

 

 

public class ValueStackAction extends ActionSupport {
    @Override
    public String execute() throws Exception {
 
        HttpServletRequest request = ServletActionContext.getRequest();
        ValueStack vs = (ValueStack) request.getAttribute("struts.valueStack");
 
        UserInfo info = new UserInfo();
        info.setUsername("呵呵");
        info.setPassword("12345");
           
        vs.getRoot().add(info);
       
        return SUCCESS;
    }

 

 

index.jsp页面进行取数据:

 

 

<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<%@ taglib uri="/struts-tags" prefix="s" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <base href="<%=basePath%>">
   
    <title>My JSP 'index.jsp' starting page</title>
   
  </head>
 
  <body>
    valueStack
      <s:property value="username"/>
  
    <s:debug></s:debug>
  </body>
</html>

 

 

 

 






双列集合:

 

index.jsp页面:需注意的是双列集合通过#获取数据

 

使用ognl表达式:

1)“#”符号有三种用途

a、访问非根对象(struts中值栈为根对象),eg:OGNL上下文和Action上下文,#相当于ActionContext.getContext();下表有几个ActionContext中有用的属性:

名称   作用域 

eg:

1-1)parameters :包含当前HTTP请求参数的Map

#parameters.id[0]=>作用相当于request.getParameter("id")

 

1-2)request:包含当前HttpServletRequest的属性(attribute)的Map

#request.userName=>作相当于于request.getAttribute("userName");

 

1-3)session:包含当前HttpSession属性(attribute)的Map

#session.userName=>session.getAttribute("userName");

 

1-4)application:包含当前应用的ServletContext的属性(attribute)的Map

#application.userName=>application.getAttribute("userName");

三:数据的流出

         当Action 完成业务处理后,最终会返回一个结果,并生成页面。最关键的地方在于在处理请求的过程中,所有业务数据对象都保留在值栈中值栈充当了一个容器,,通过它在框架的各个地方都可以随时访问这些业务对象,在生成页面的过程中,页面标签都可以访问值栈,并通过OGNL表达式获取对象的值。

四:Value Stack值栈

1.什么是值栈:

值栈就是框架创建的一个存储区域,用来保存Model对象,它具有栈的特性,可以存放多个对象,,但是要按照先后顺序压入栈的。框架处理每个请求时,都会创建对应的运行环境,这是会创建值栈和请求对应的Action 实例,并将每个Action实例压入值栈中。值栈是一个虚拟对象,它可以暴露它所包含的所有对象的的属性,就好像这些属性是它自己的一样。

2.OGNL优先使用:

OGNL访问值栈时从它的上往下访问不管值栈是否有关重复的靠近值栈最上面的属性都

会被优先使用。

五:类型转换器

  1. 为什么要使用类型转换器:

在基于HTTP协议的web应用中,客户端请求的所有内容,均以文本编码的形式传入到服务器端,但服务器端的编程语言,有着丰富的数据类型,如int,boolean,Date及自定义类型等,因此当这些参数进入应用程序时,它们必须转换为合适的服务器端编程语言的数据库类型。

  1. 内置类型转换器:

(1.)Stirng:将int,long,double,Boolean,String类型的数组,或java.util.Date类型转换为字符串。

(2.)boolean/Boolean:在字符串和布尔类型之间进行类型转换。

(3.)char/Character:在字符串和字符串之间进行转换。

(4.)int /Integer,float/Fload,long/Long,double/Doubl:在字符串和数值之间进行转换。

(5.)Date:在字符串和日期类型之间进行转换,具体输入/输出格式与当前的Local相关。

(6.)数组和集合:在字符串数组和数组对象,集合对象之间转换。

1.原始类型和包装类     

示例如下:

public class ShowOrginalAndJavaBeanAction extends ActionSupport {
         private User user;
         private String msg;
         public User getUser() {
                   return user;
         }
         public void setUser(User user) {
                   this.user = user;
         }
         public String getMsg() {
                   return msg;
         }
         public void setMsg(String msg) {
                   this.msg = msg;
         }
         public String execute(){
                   User user=new User();
                   user.setName("accp");
                   user.setAge(22);
                  
                   Adderss address=new Adderss();
                   address.setGj("中国");
                   address.setCity("北京");
                   address.setStreet("城府路");
                   //user.setAdderss(adderss);
                   setMsg("展示原始类型和JavaBean");
                   return SUCCESS;
         }
}

页面展示代码如下:

<body>

    <h2>直接量:</h2>

    <s:property value="msg" /><br>

    姓名:<s:property value="user.name" /><br/>

    年龄:<s:property value="user.age" /><br/>

    国家:<s:property value="user.address.gj" /><br/>

    城市:<s:property value="user.address.city" /><br/>

    街道:<s:property value="user.address.street" /><br/>

  </body>

  1. 多值类型请求参数的处理

(1.)数组的方式:

    

public class ArrayAction extends ActionSupport{
private String[] hobbies;
private Double[] numbers=new Double[3];
public String[] getHobbies() {
           return hobbies;
}
public void setHobbies(String[] hobbies) {
           this.hobbies = hobbies;
}
public Double[] getNumbers() {
           return numbers;
}
public void setNumbers(Double[] numbers) {
           this.numbers = numbers;
}
public String execute(){
           for (int i = 0; i < hobbies.length; i++) {
                    System.out.println("数组第:"+i+"元素,为"+hobbies[i]);
           }
           System.out.println();
           for (int i = 0; i < numbers.length; i++) {
                    System.out.println("第"+i+"个元素,为"+numbers[i]);
           }
           return SUCCESS;
}
}

 

页面显示代码如下:

<body>
    <h2>数组:</h2>
    <s:form action="arrayAction" method="post" />
    <s:textfield name="hobbies" label="爱好:" />
    <s:textfield name="hobbies" label="爱好:" />
    <s:textfield name="hobbies" label="爱好:" />
    ---------------------------
<s:textfield name="numbers[0]" label="数字:" />
<s:textfield name="numbers[1]" label="数字:" />
<s:textfield name="numbers[2]" label="数字:" />
<s:submit value="提交"></s:submit>
  </body>

 

(2.)集合的方式:

       

public class ListAction extends ActionSupport{
private List hobbies;
private List<Integer> numbers;
private List<User> users;
 
public List getHobbies() {
           return hobbies;
}
 
public void setHobbies(List hobbies) {
           this.hobbies = hobbies;
}
 
public List<Integer> getNumbers() {
           return numbers;
}
 
public void setNumbers(List<Integer> numbers) {
           this.numbers = numbers;
}
 
public List<User> getUsers() {
           return users;
}
 
public void setUsers(List<User> users) {
           this.users = users;
}
 
public String execute1(){
           for (int i = 0; i < hobbies.size(); i++) {
                    System.out.println("数组第:"+i+"元素,为"+hobbies.get(i));
           }
           System.out.println();
           for (int i = 0; i < numbers.size(); i++) {
                    System.out.println("第"+i+"个元素,为"+numbers.get(i));
           }
          
           System.out.println();
           for (int i = 0; i < numbers.size(); i++) {
                    System.out.println("第"+i+"个元素,为"+users.get(i).getName());
           }
           return SUCCESS;
}
}

 

页面显示代码如下:

<body>
    <h2>数组:</h2>
    <s:form action="listyAction" method="POST" />
    <s:textfield name="hobbies" label="爱好:" />
    <s:textfield name="hobbies" label="爱好:" />
    <s:textfield name="hobbies" label="爱好:" />
    ---------------------------
<s:textfield name="numbers[0]" label="数字:" />
<s:textfield name="numbers[1]" label="数字:" />
<s:textfield name="numbers[2]" label="数字:" />
---------------------------
<s:textfield name="user.name" label="姓名:" />
<s:textfield name="user.name" label="姓名:" />
<s:textfield name="user.name" label="姓名:" />
<s:submit value="提交"></s:submit>
  </body>

六:自定义转换器:

  1. 创建自定义转换器
  2. 配置自定义转换器
  3. 创建和配置日期转换器

示例如下:在此之前先创建一个这样的类:

public class DateAction extends ActionSupport{
private String currentDate;
 
public String getCurrentDate() {
           return currentDate;
}
 
public void setCurrentDate(String currentDate) {
           this.currentDate = currentDate;
}
public String dates(){
           return SUCCESS;
}
}

然后在创建:

public class DateConverter extends StrutsTypeConverter {

private final DateFormat[] dfs={

           new SimpleDateFormat("yyyy年MM月dd日"),

           new SimpleDateFormat("yyyy-MM-dd"),

           new SimpleDateFormat("MM/dd/yy"),

new SimpleDateFormat("yyyy.MM.dd"),
           new SimpleDateFormat("yyyyMMdd"),
           new SimpleDateFormat("yyyy/MM/dd")
};
/*
 * 将指定格式的字符串转换为日期类型
 * (non-Javadoc)
 * @see org.apache.struts2.util.StrutsTypeConverter#convertFromString(java.util.Map, java.lang.String[], java.lang.Class)
 */
@Override
public Object convertFromString(Map context, String[] values, Class toType) {
           // TODO Auto-generated method stub
           String dateStr=values[0]; //获取日期的字符串
           for (int i = 0; i < dfs.length; i++) { //遍历日期支持的格式进行转换
                    try {
                             return dfs[i].parse(dateStr);
                    } catch (Exception e) {
                             // TODO: handle exception
                             continue;
                    }
           }
           //如果遍历完毕后仍没有转换成功,表明出现转换异常
           throw new TypeConversionException();
}
/*
 * 将日期格式转换为指定字符串
 * (non-Javadoc)
 * @see org.apache.struts2.util.StrutsTypeConverter#convertToString(java.util.Map, java.lang.Object)
 */
 
@Override
public String convertToString(Map context, Object object) {
           // TODO Auto-generated method stub
           Date date=(Date)object;
           //输出的格式是yyyy--MM-dd
           return new SimpleDateFormat("yyyy-MM-dd").format(date);
}
}

 

 

页面显示代码如下

<body>

    <s:form action="datesAction" />

    <h2>自定义类型转换器</h2><br/>

    请输入日期:<s:textfield name="currentDate" />

    <s:submit value="提交" />

  </body

七:处理类型转换器错误

  1. 前提条件:如果在服务器端判断类型转换错误。则需要满足一下条件。

(1.)启动StrutsConversionErrorLnterceptor拦截器。该拦截器已经包含在defaultStack拦截器栈中,参看Struts-default.xml文件,如果拓展此包启动时自动加载。

         (2.)实现ValidationAware接口,ActionSupport实现了该接口。

         (3.)配置input结果映射。出现类型转换错误将在所配置页面显示错误的信息,如果没有配置将出现错误的提示,提示没有指定的input页面。

         (4.)在页面使用Struts 2表单标签或使用<s:fielderror>标签输出转换错误。Struts 2表单标签内嵌了输出错误的信息功能。

2.修改所有类型的转换错误信息。

         可修改默认文本信息:

                  <constant  name=”struts.custom.il8n.resources” value=”message”>

                   然后在src目录下创建资源文件message.properties并添加文本如下:

                   Xwork.default.invalid.fieldvalue=字段”{0}”的值无效

3.制定特定字段类型转换错误信息。

         Invalid.fieldvalue.timeDate=日期转换错误

八:OGNL表达式

  1. 表达式语音

(1.)访问Bean属性:代码如下

           <s:form   action=”Register”>

           <s:textfield  name=”user.address.streetName” label=”请输入街道名称.”  />

                   <s:submit  />

                   </s:form>

(2.)访问集合对象

public class ListAction extends ActionSupport{
private List hobbies;
private List<Integer> numbers;
private List<User> users;
 
public List getHobbies() {
           return hobbies;
}
 
public void setHobbies(List hobbies) {
           this.hobbies = hobbies;
}
 
public List<Integer> getNumbers() {
           return numbers;
}
 
public void setNumbers(List<Integer> numbers) {
           this.numbers = numbers;
}
 
public List<User> getUsers() {
           return users;
}
 
public void setUsers(List<User> users) {
           this.users = users;
}
 
public String execute1(){
           for (int i = 0; i < hobbies.size(); i++) {
                    System.out.println("数组第:"+i+"元素,为"+hobbies.get(i));
           }
           System.out.println();
           for (int i = 0; i < numbers.size(); i++) {
                    System.out.println("第"+i+"个元素,为"+numbers.get(i));
           }
          
           System.out.println();
           for (int i = 0; i < numbers.size(); i++) {
                    System.out.println("第"+i+"个元素,为"+users.get(i).getName());
           }
           return SUCCESS;
}
}

 

页面代码如下:

      

<body>
         !!!!!!迭代数组输出:
         <h1>!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!</h1>
         <br />
         <s:iterator value="likes">
                   <s:property />
                   <br />
         </s:iterator>
         <h1>!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!</h1>
         !!!!!!迭代集合(元素类型为Double):
         <br />
 
         <s:iterator value="numbers">
                   <s:property />
                   <br />
         </s:iterator>
         <h1>!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!</h1>
         !!!!!!迭代集合(元素类型为User):
         <br />
 
         <s:iterator value="users">
          !!!!!! 用户名:<s:property value="name" />
                   <br />
          !!!!!!年龄:<s:property value="age" />
                   <br />
          !!!!!!国家:<s:property
                            value="address.country" />
                   <br />
          !!!!!!城市:<s:property value="address.city" />
                   <br />
                   <h1>!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!</h1>
         </s:iterator>
 
         <s:debug></s:debug>
</body>

(3.)对于集合对象的访问,在开发过程中往往还有一些几种情况

                   访问列表或者数组的某一个元素,可以使用属性[index]的方式。如userList[1].name

                   访问Map的某一个元素。可以使用属性名[key]如userMap[‘userA’].name   

通过size或者length来访问集合的长度。

九:访问ActionContext中的数据