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