Struts2 OGNL
Struts2 OGNL是表达语言,其中OGNL代表对象图导航语言。
OGNL与Struts2紧密耦合,用于将表单参数作为Java bean变量存储在ValueStack中,并在结果页面中从ValueStack检索值。
Struts2 OGNL
Struts2 OGNL执行两项重要任务-数据传输和类型转换。
Struts2中的OGNL从servlet请求中获取请求参数,并将其传输到相应的java变量。
由于我们以String形式获取请求参数,而Java Bean变量可以是String,int,数组,列表或者任何自定义对象,因此类型转换也是一项重要任务,OGNL会通过其内置的类型转换器来处理类型转换。
Struts2 OGNL非常灵活,我们可以轻松地对其进行扩展以创建我们自己的自定义转换器类。
我们将首先使用基本数据类型(例如String,boolean,int,数组和列表)来研究OGNL的用法。
然后,我们将为自定义java bean变量创建自己的转换器类。
Struts2 OGNL示例
我们针对Struts2 OGNL示例的最终项目结构如下图所示。
Struts 2配置文件
web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xmlns="https://java.sun.com/xml/ns/javaee" xsi:schemaLocation="https://java.sun.com/xml/ns/javaee https://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0"> <display-name>Struts2OGNLExample</display-name> <filter> <filter-name>struts2</filter-name> <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>
pom.xml
<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>Struts2OGNLExample</groupId>
<artifactId>Struts2OGNLExample</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<dependencies>
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-core</artifactId>
<version>2.3.15.1</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>2.3</version>
<configuration>
<warSourceDirectory>WebContent</warSourceDirectory>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
</plugins>
<finalName>${project.artifactId}</finalName>
</build>
</project>
struts.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN" "https://struts.apache.org/dtds/struts-2.3.dtd"> <struts> <constant name="struts.devMode" value="false"></constant> <constant name="struts.convention.result.path" value="/"></constant> <package name="user" namespace="/" extends="struts-default"> <action name="home"> <result>/home.jsp</result> </action> <action name="welcome" class="com.theitroad.struts2.actions.WelcomeAction"> <result name="success">/welcome.jsp</result> </action> </package> </struts>
配置文件是易于理解的,它们只是为了配置我们的应用程序以使用Struts 2框架。
Struts2 OGNL示例模型类
package com.theitroad.struts2.model;
public class Data {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
package com.theitroad.struts2.model;
public class Rectangle {
private int x;
private int y;
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
}
package com.theitroad.struts2.model;
import java.util.Date;
import java.util.List;
import java.util.Map;
public class MyJavaBean {
private String name;
private boolean flag;
private Integer age;
private Date date;
private String[] stocks;
//roles array needs to initialize because it's used with index in form
private String[] roles = new String[5];
//do not preinitialize lists or any collections
private List<Data> usersList;
private List<Data> fruitsList;
private Map<String, Data> usersMap;
//custom type converter example
private Rectangle rectangle;
public List<Data> getFruitsList() {
return fruitsList;
}
public void setFruitsList(List<Data> fruitsList) {
this.fruitsList = fruitsList;
}
public List<Data> getUsersList() {
return usersList;
}
public void setUsersList(List<Data> users) {
this.usersList = users;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
public String[] getStocks() {
return stocks;
}
public void setStocks(String[] stocks) {
this.stocks = stocks;
}
public String[] getRoles() {
return roles;
}
public void setRoles(String[] roles) {
this.roles = roles;
}
public Map<String, Data> getUsersMap() {
return usersMap;
}
public void setUsersMap(Map<String, Data> usersMap) {
this.usersMap = usersMap;
}
public Rectangle getRectangle() {
return rectangle;
}
public void setRectangle(Rectangle rectangle) {
this.rectangle = rectangle;
}
}
MyJavaBean是我们将要使用的动作Bean类,请注意类型为Rectangle的变量。
由于这是一个自定义类,因此我们需要为此实现自己的转换器类。
稍后我们将对此进行调查。
Struts2 OGNL示例动作类
package com.theitroad.struts2.actions;
import com.theitroad.struts2.model.MyJavaBean;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;
public class WelcomeAction extends ActionSupport implements ModelDriven<MyJavaBean>{
public String execute(){
return SUCCESS;
}
private MyJavaBean bean = new MyJavaBean();
@Override
public MyJavaBean getModel() {
return bean;
}
}
动作类仅返回成功页面,此处没有逻辑。
Struts2 OGNL示例结果页面
home.jsp
<%@ page language="java" contentType="text/html; charset=US-ASCII" pageEncoding="US-ASCII"%> <%@ taglib uri="/struts-tags" prefix="s" %> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "https://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=US-ASCII"> <title>Home Page Form</title> </head> <body> <h3>Struts 2 OGNL Examples</h3> <s:form action="welcome" method="post"> <table> <s:textfield name="name" label="Name" ></s:textfield> <s:textfield name="flag" label="True/False?"></s:textfield> <s:textfield name="age" label="Current Year?"></s:textfield> <s:textfield name="date" label="Todays Date (mm/dd/yyyy)?"></s:textfield> </table> <h3>Struts 2 Array OGNL Example</h3> Array with same name example<br> <table> <s:textfield name="stocks" label="Stock 1"></s:textfield> <s:textfield name="stocks" label="Stock 2"></s:textfield> <s:textfield name="stocks" label="Stock 3"></s:textfield> </table> Array with indexed name example<br> <table> <s:textfield name="roles[0]" label="Role 1"></s:textfield> <s:textfield name="roles[1]" label="Role 2"></s:textfield> <s:textfield name="roles[2]" label="Role 3"></s:textfield> </table> <h3>Struts 2 List OGNL Example</h3> List with same name example<br> <table> <s:textfield name="usersList.name" label="User 1 Name"></s:textfield> <s:textfield name="usersList.name" label="User 2 Name"></s:textfield> <s:textfield name="usersList.name" label="User 3 Name"></s:textfield> </table> List with indexed name example<br> <table> <s:textfield name="fruitsList[0].name" label="Fruit 1"></s:textfield> <s:textfield name="fruitsList[1].name" label="Fruit 2"></s:textfield> <s:textfield name="fruitsList[2].name" label="Fruit 3"></s:textfield> </table> Map Example <table> <s:textfield name="usersMap['first'].name" label="User 1"></s:textfield> <s:textfield name="usersMap['second'].name" label="User 2"></s:textfield> <s:textfield name="usersMap['third'].name" label="User 3"></s:textfield> </table> Custom Converter Example <table> <s:textfield name="rectangle" label="Rectangle in format R:x,y"></s:textfield> </table> <s:submit label="Submit" align="left"></s:submit> </s:form> </body> </html>
home.jsp用作输入页面,用户可以其中提供值并调用欢迎操作。
welcome.jsp
<%@ page language="java" contentType="text/html; charset=US-ASCII"
pageEncoding="US-ASCII"%>
<%@ taglib uri="/struts-tags" prefix="s" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "https://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=US-ASCII">
<title>Welcome Page Example</title>
</head>
<body>
<h3>Struts 2 OGNL Examples</h3>
Name = <s:property value="name"></s:property><br>
True/False? = <s:property value="flag" ></s:property><br>
Current Year? = <s:property value="age"></s:property><br>
Todays Date (mm/dd/yyyy)? = <s:date name="date" format="MM/dd/yyyy"></s:date><br>
Stocks Array = <s:property value="stocks"<br>
Roles Array = <s:property value="roles"<br>
Users List = <s:iterator value="usersList"><s:property value="name" </s:iterator><br>
Fruits List = <s:iterator value="fruitsList"><s:property value="name" </s:iterator><br>
Fruit 1 Name = <s:property value="fruitsList[0].name"<br>
Users Map = <s:iterator value="usersMap">{<s:property value="key",<s:property value="value.name"} </s:iterator><br>
Rectangle Dimensions: x = <s:property value="rectangle.x" and y = <s:property value="rectangle.y"<br>
</body>
</html>
welcome.jsp仅用于显示用户使用的值,以证明OGNL正在处理数据传输和类型转换。
在继续讨论自定义转换器类之前,让我们看一下上述实现中的一些要点。
基本数据类型的转换是自动的,我们不需要遵循任何特殊的规则。
Struts 2还负责将String转换为Date,我们可以使用s:date以特定格式显示它。
数组和列表可以与名称和索引一起使用。
如果使用索引,则需要在bean中初始化数组。
因此,在bean类中初始化了角色数组。不要在bean中初始化列表变量,否则会抛出错误。
OGNL负责初始化和填充值。Struts2中的OGNL还提供了对Map的内置支持,我们可以在结果页中使用它。
我们可以将迭代器与List,Map,Array等多值数据类型一起使用来遍历它们。
我们可以使用索引或者键从这些变量中获取特定值。
Struts2 OGNL自定义类型转换器
创建和配置自定义类型转换器类非常容易。
第一步是修复自定义类的输入格式。
在我的示例中,我将用户输入固定为" R:x,y",其中x和y是矩形变量,应为整数。
第二步是实现转换器类。
类型转换器类应实现com.opensymphony.xwork2.conversion.TypeConverter接口。
由于在Web应用程序中,我们总是以String的形式获取请求,并以String的形式发送响应,因此Struts 2 API提供了TypeConverter接口的默认实现,即StrutsTypeConverter。
StrutsTypeConverter包含两个抽象方法– convertFromString将String转换为Object,convertToString将Object转换为String。
我们将为自定义类型转换器扩展此类。
package com.theitroad.struts2.typeconverters;
import java.util.Map;
import org.apache.struts2.util.StrutsTypeConverter;
import com.theitroad.struts2.model.Rectangle;
import com.opensymphony.xwork2.conversion.TypeConversionException;
/**
* Custom type converter to convert user input to Rectangle
* Format is R:x,y where x and y are int defining Rectangle dimensions
* @author hyman
*
*/
public class RectangleTypeConverter extends StrutsTypeConverter {
@Override
public Object convertFromString(Map arg0, String[] inputs, Class arg2) {
String input = inputs[0];
if(!input.startsWith("R:")) throw new TypeConversionException("invalid input");
input = input.substring(2);
String[] dimensions = input.split(",");
int x = Integer.parseInt(dimensions[0]);
int y = Integer.parseInt(dimensions[1]);
Rectangle rect = new Rectangle();
rect.setX(x);
rect.setY(y);
return rect;
}
@Override
public String convertToString(Map arg0, Object obj) {
Rectangle rect = (Rectangle) obj;
String output = "R:" + rect.getX() + "," + rect.getY();
return output;
}
}
请注意,代码非常简单,可以将输入字符串解析为对象,反之亦然。
下一步是配置要用于Rectangle类型变量的类型转换器。
有两种配置方式–第一种是针对特定操作进行配置,第二种方式是进行全局配置。
对于特定于动作的转换器,我们可以使用com.opensymphony.xwork2.conversion.annotations.TypeConversion注释,并如下更改setter方法。
@TypeConversion(converter="com.theitroad.struts2.typeconverters.RectangleTypeConverter")
public void setRectangle(Rectangle rectangle) {
this.rectangle = rectangle;
}
用于ModelDriven动作类的自定义类型转换器
如果Action类正在为Java bean实现ModelDriven接口,则另一种方法是创建名称为{JavaBeanName} -conversion.properties的属性文件,并将其放入与Java bean类相同的包中,因此我们可以创建MyJavaBean-conversion.properties和将其放入带有以下数据的com.theitroad.struts2.model包中。
MyJavaBean-conversion.properties
#For Action Classes implementing ModelDriven<MyJavaBean> #variable-name=TypeConverter class name rectangle=com.theitroad.struts2.typeconverters.RectangleTypeConverter
如我在本项目中所做的那样,要进行全局转换,我们需要创建xwork-conversion.properties属性文件,并确保它位于WEB-INF/classes目录中。
我们需要提供类名和转换器作为键值对。
对我们来说
xwork-conversion.properties
#Application level custom converter configuration com.theitroad.struts2.model.Rectangle=com.theitroad.struts2.typeconverters.RectangleTypeConverter

