Struts2

Posted 57容杰龙

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Struts2相关的知识,希望对你有一定的参考价值。

以下内容是基于导入struts2-2.3.32.jar包来讲的

1.OGNL

  OGNL是Object-Graph Navigation Language的缩写,全称为对象图导航语言,是一种功能强大的表达式语言,它通过简单一致的语法,可以任意存取对象的属性或者调用对象的方法,能够遍历整个对象的结构图,实现对象属性类型的转换等功能。它是一个开源项目。 Struts2框架使用OGNL作为默认的表达式语言  

  OGNL 有一个上下文(Context)概念,说白了上下文就是一个MAP结构,它实现了 java.utils.Map 的接口。

作用:

1、支持对象方法调用,如xxx.doSomeSpecial();
2、支持类静态的方法调用和值访问,表达式的格式:
 @[类全名(包括包路径)]@[方法名 |  值名],例如:
    @java.lang.String@format(\'foo %s\', \'bar\')
    或@tutorial.MyConstant@APP_NAME;
3、支持赋值操作和表达式串联,如price=100, discount=0.8,
     calculatePrice(),这个表达式会返回80;
4、访问OGNL上下文(OGNL context)和ActionContext;
5、操作集合对象。

ognl表达式取值,如果是根元素取值不用带#符号, 非根元素取值要带#号!

知识点:

1.OGNL表达式的计算是围绕OGNL上下文进行的。
OGNL上下文实际上就是一个Map对象,由ognl.OgnlContext类表示。它里面可以存放很多个JavaBean对象。它有一个上下文根对象。
上下文中的根对象可以直接使用名来访问或直接使用它的属性名访问它的属性值。否则要加前缀“#key”。
 
2.Struts2的标签库都是使用OGNL表达式来访问ActionContext中的对象数据的。如:<s:propertyvalue="xxx"/>。
 
3.Struts2将ActionContext设置为OGNL上下文,并将值栈作为OGNL的根对象放置到ActionContext中。
 
4.值栈(ValueStack) :
可以在值栈中放入、删除、查询对象。访问值栈中的对象不用“#”
Struts2总是把当前Action实例放置在栈顶。所以在OGNL中引用Action中的属性也可以省略“#”。
 
5.调用ActionContext的put(key,value)放入的数据,需要使用#访问。

OGNL中重要的3个符号:#、%、$:

#、%和$符号在OGNL表达式中经常出现,而这三种符号也是开发者不容易掌握和理解的部分,需要时间的积累才渐渐弄清楚。
1.#符号

#符号的用途一般有三种。

—    访问非根对象属性,例如#session.msg表达式,由于Struts 2中值栈被视为根对象,所以访问其他非根对象时,需要加#前缀。实际上,#相当于ActionContext. getContext();#session.msg表达式相当于ActionContext.getContext().getSession(). getAttribute("msg") 。

—    用于过滤和投影(projecting)集合,如persons.{?#this.age>25},persons.{?#this.name==\'pla1\'}.{age}[0]。

—    用来构造Map,例如示例中的#{\'foo1\':\'bar1\', \'foo2\':\'bar2\'}。

2.%符号

%符号的用途是在标志的属性为字符串类型时,计算OGNL表达式的值,这个类似js中的eval,很暴力。

3.$符号

$符号主要有两个方面的用途。

—    在国际化资源文件中,引用OGNL表达式,例如国际化资源文件中的代码:reg.agerange=国际化资源信息:年龄必须在${min}同${max}之间。

—    在Struts 2框架的配置文件中引用OGNL表达式。

2.基本使用

A.往根元素设置并获取值:

 1 package com.rong.web.test;
 2 
 3 import com.rong.web.entity.User;
 4 
 5 import ognl.Ognl;
 6 import ognl.OgnlContext;
 7 
 8 public class OGNLTest {
 9 
10     public static void main(String[] args) throws Exception{
11         //Ognl上下文OgnlContext实现了  java.util.Map 的接口,具有Map的功能
12         OgnlContext ognlContext = new OgnlContext();
13         ognlContext.put("username", "huge");
14         System.out.println(ognlContext.get("username"));
15         
16         User user=new User();
17         user.setId(57);
18         user.setName("rjl");
19         user.setAge(18);
20         //设置上下文根对象
21         ognlContext.setRoot(user);
22         //表达式
23         Object expression1 = Ognl.parseExpression("name");
24         Object expression2 = Ognl.parseExpression("id");
25         Object expression3 = Ognl.parseExpression("age");
26         Object value1 = Ognl.getValue(expression1, ognlContext, ognlContext.getRoot());
27         Object value2 = Ognl.getValue(expression2, ognlContext, ognlContext.getRoot());
28         Object value3 = Ognl.getValue(expression3, ognlContext, ognlContext.getRoot());
29 
30         System.out.println(value1.toString()+value2.toString()+value3.toString());
31     }
32 }

 B.往非根元素设置并获取值:

 1 package com.rong.web.test;
 2 
 3 import com.rong.web.entity.Student;
 4 
 5 import ognl.Ognl;
 6 import ognl.OgnlContext;
 7 
 8 public class OGNLTest {
 9 
10     public static void main(String[] args) throws Exception{
11         //Ognl上下文OgnlContext实现了  java.utils.Map 的接口,具有Map的功能
12         OgnlContext ognlContext = new OgnlContext();
13         //往非根元素设置值;非根元素取值要带#号
14         Student student = new Student(24,"kobe",38);
15         ognlContext.put("stu", student);
16         Object expression = Ognl.parseExpression("#stu.sid");
17         Object value = Ognl.getValue(expression, ognlContext, ognlContext.getRoot());
18         System.out.println(value);
19     }
20 }

3.值栈(ValueStack)

  ValueStack实际是一个接口,在Struts2中利用OGNL时,实际上使用的是实现了该接口的OgnlValueStack类,这个类是Struts2利用OGNL的基础。

ValueStack贯穿整个 Action 的生命周期
(每个 Action 类的对象实例都拥有一个ValueStack 对象).
相当于一个数据的中转站. 在其中保存当前Action 对象和其他相关对象.
Struts2框架把 ValueStack 对象保存在名为 “struts.valueStack” 的request请求属性中。

Action中获取值栈对象的两种方式:

 1 package com.rong.web.action;
 2 
 3 import javax.servlet.http.HttpServletRequest;
 4 
 5 import org.apache.struts2.ServletActionContext;
 6 
 7 import com.opensymphony.xwork2.ActionContext;
 8 import com.opensymphony.xwork2.ActionSupport;
 9 import com.opensymphony.xwork2.util.ValueStack;
10 
11 public class MyAction extends ActionSupport {
12     private static final long serialVersionUID = -351587239525292420L;
13     
14     @Override
15     public String execute() throws Exception {
16         ActionContext actionContext = ActionContext.getContext();
17         //ValueStack是执行Ognl表达式的基础。也是从OgnlContext中去获取值、设置值的基础
18         //ValueStack值栈
19         ValueStack valueStack = actionContext.getValueStack();
20         //com.opensymphony.xwork2.ognl.OgnlValueStack@55195240
21         System.out.println(valueStack);
22         
23         
24         //Struts2框架把 ValueStack 对象保存在名为 “struts.valueStack” 的request请求属性中。
25         HttpServletRequest request = ServletActionContext.getRequest();
26         Object object = request.getAttribute("struts.valueStack");
27         //com.opensymphony.xwork2.ognl.OgnlValueStack@55195240
28         System.out.println(object);
29         return SUCCESS;
30     }
31 }

ObjectStack: Struts 把动作和相关对象压入 ObjectStack 中--List
ContextMap: Struts 把各种各样的映射关系(一些 Map 类型的对象) 压入 ContextMap 中
Struts 会把下面这些映射压入 ContextMap 中
  parameters: 该 Map 中包含当前请求的请求参数
  request: 该 Map 中包含当前 request 对象中的所有属性
  session: 该 Map 中包含当前 session 对象中的所有属性
  application:该 Map 中包含当前 application 对象中的所有属性
  attr: 该 Map 按如下顺序来检索某个属性: request, session, application

 

值栈中有根元素 和 非根元素。
非根元素就是OgnlContext

OgnlContext中引用了值栈的根元素数据

Struts每次访问action的时候:
1. 创建一个ActionContext对象
2. 创建值栈对象
  把域中存放的数据、以及Action对象,放到值栈中!
  这时候,值栈就有了struts运行时期的数据!
3. 把值栈的数据,拷贝一份给ActionContext!
4. 最后,把值栈放到request的请求中

操作根元素数据:

 1      ActionContext actionContext = ActionContext.getContext();
 2         //ValueStack是执行Ognl表达式的基础。也是从OgnlContext中去获取值、设置值的基础
 3         //ValueStack值栈
 4         ValueStack valueStack = actionContext.getValueStack();
 5         //com.opensymphony.xwork2.ognl.OgnlValueStack@55195240
 6         System.out.println(valueStack);
 7         Student student = new Student(24,"kobe",38);
 8         valueStack.pop();//移出根元素
 9         valueStack.getRoot().push(student);//设置根元素
10         
11         boolean empty = valueStack.getRoot().isEmpty();
12         System.out.println("根元素是否为空:"+empty);
13         Student pop = (Student) valueStack.getRoot().pop();//移出根元素,并返回该元素
14         System.out.println(pop.getSname());

操作非根元素数据:

 1 package com.rong.web.action;
 2 
 3 import java.util.Map;
 4 
 5 import com.opensymphony.xwork2.ActionContext;
 6 import com.opensymphony.xwork2.ActionSupport;
 7 import com.rong.web.entity.Student;
 8 
 9 public class MyAction extends ActionSupport {
10     private static final long serialVersionUID = -351587239525292420L;
11     
12     @Override
13     public String execute() throws Exception {
14         ActionContext actionContext = ActionContext.getContext();
15         Student student = new Student(24,"kobe",38);
16         Map<String, Object> request = actionContext.getContextMap();
17         request.put("one", "ONE");
18         Map<String, Object> session = actionContext.getSession();
19         session.put("two", student);
20         Map<String, Object> application = actionContext.getApplication();
21         application.put("three", "Three");
22         System.out.println(request.get("one"));
23         System.out.println(((Student)session.get("two")).getSname());
24         System.out.println(application.get("three"));
25         
26         return SUCCESS;
27     }
28 }

4.struts标签(实际开发很少用,用jstl较多)

标签防止重复提交

<s:token />标签防止重复提交,用法如下:第一步:在表单中加入<s:token />
<s:form action="helloworld_other" method="post" namespace="/test">
<s:textfield name="person.name"/><s:token/><s:submit/>
</s:form>
第二步:
<action name="helloworld_*" class="com.rong.web.action.HelloWorldAction" method="{1}">
<interceptor-ref name="defaultStack"/>
<!-- 增加令牌拦截器 -->
<interceptor-ref name="token">
<!-- 哪些方法被令牌拦截器拦截 -->
<param name=“includeMethods">save</param>
</interceptor-ref>
<!-- 当表单重复提交转向的页面 -->
<result name="invalid.token">/WEB-INF/page/message.jsp</result>
</action>
以上配置加入了“token”拦截器和“invalid.token”结果,因为“token”拦截器在会话的token与请求的token不一致时,将会直接返回“invalid.token”结果

在debug状态,控制台出现下面信息,是因为Action中并没有struts.token和struts.token.name属性,我们不用关心这个错误:
严重: ParametersInterceptor - [setParameters]: Unexpected Exception caught setting \'struts.token\' on \'class xxx: Error setting expression \'struts.token\' with value \'[Ljava.lang.String;@39f16f\'
严重: ParametersInterceptor - [setParameters]: Unexpected Exception caught setting \'struts.token.name\'

$符号配置文件取值用:

 1 package com.rong.web.action;
 2 
 3 import com.opensymphony.xwork2.ActionSupport;
 4 
 5 public class MyAction extends ActionSupport {
 6     private static final long serialVersionUID = -351587239525292420L;
 7     private String username;
 8     public String getUsername() {
 9         return username;
10     }
11     public void setUsername(String username) {
12         this.username = username;
13     }
14     @Override
15     public String execute() throws Exception {
16         username="rjl";
17         return SUCCESS;
18     }
19 }
<struts>
    <package name="default" namespace="/" extends="struts-default" >
        <action name="test" class="com.rong.web.action.MyAction">
            <result type="redirect">/one.jsp?username=${username}</result>
        </action>
    </package>
</struts>

#号%号略。

struts标签使用:

导入标签库:<%@ taglib uri="/struts-tags" prefix="s"%>

1、条件标签 if…elseif…else

 1 package com.rong.web.action;
 2 
 3 import java.util.Map;
 4 
 5 import com.opensymphony.xwork2.ActionContext;
 6 import com.opensymphony.xwork2.ActionSupport;
 7 
 8 public class MyAction extends ActionSupport {
 9     private static final long serialVersionUID = -351587239525292420L;
10     @Override
11     public String execute() throws Exception {
12         ActionContext actionContext = ActionContext.getContext();
13         Map<String, Object> session = actionContext.getSession();
14         session.put("number", 57);
15         return SUCCESS;
16     }
17 }
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="/struts-tags" prefix="s"%>
<c:set var="basePath" value="${pageContext.request.contextPath }"></c:set>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
    one.jsp!
    <s:debug></s:debug>
    <s:if test="#session.number>57">great!</s:if>
    <s:elseif test="#session.number<57">less!</s:elseif>
    <s:else>equal!</s:else>
</body>
</html>

2、迭代标签 iterator

用于循环数组,集合和Map

属性:  

value:数组,集合或Map

var:当前元素

status:当前元素的状态index,count,even,odd,first,last

其余的标签略。

5.Action中验证(代码验证)

后台向前台返回校验信息,数据校验由后台进行(较少用,一般前台做数据校验)

A.验证所有方法

<struts>
    <package name="default" namespace="/" extends="struts-default" >
        <action name="check" class="com.rong.web.action.MyAction">
            <result>/one.jsp</result>
            <!-- 验证信息错误时,框架固定返回input视图 -->
            <result name="input">/index.jsp</result>
        </action>
    </package>
</struts>

index.jsp

<body>
    <form action="${pageContext.request.contextPath }/check" method="post">
        <input type="text" name="student.sname"/>
        <!-- 获取后台返回的校验信息 -->
        <s:fielderror key="#student.sname"></s:fielderror>
        <input type="submit" value="登录"/>
    </form>
</body>

MyAction.java

 1 package com.rong.web.action;
 2 
 3 import com.opensymphony.xwork2.ActionSupport;
 4 import com.rong.web.entity.Student;
 5 
 6 public class MyAction extends ActionSupport {
 7     private static final long serialVersionUID = -351587239525292420L;
 8     private Student student;
 9     public Student getStudent() {
10         return student;
11     }
12     public void setStudent(Student student) {
13         this.student = student;
14     }
15     //重写validate方法校验前台信息,在执行execute方法之前执行。此Action里所有的方法都会走这个验证。
16     @Override
17     public void validate() {
18         if(student.getSname().trim().length()==0){
19             //向前台返回校验信息
20             this.addFieldError("student.sname", "学生名字不能为空!");
21         }
22     }
23     @Override
24     public String execute() throws Exception {
25         System.out.println("execute!!!");
26         return SUCCESS;
27     }
28 }

B.验证某个方法

public void validate方法名(){

  //方法名首字母需要大写!!!

}

 1 package com.rong.web.action;
 2 
 3 import com.opensymphony.xwork2.ActionSupport;
 4 import com.rong.web.entity.Student;
 5 
 6 public class MyAction extends ActionSupport {
 7     private static final long serialVersionUID = -351587239525292420L;
 8     private Student student;
 9     public Student getStudent() {
10         return student;
11     }
12     public void setStudent(Student student) {
13         this.student = student;
14     }
15     //针对某一个方法做验证:validate方法名,其中方法名首字母要大写
16     public void validateExecute(){
17         if(student.getSname().trim().length()==0){
18             //向前台返回校验信息
19             this.addFieldError("student.sname", "学生名字不能为空!");
20         }
21     }
22     @Override
23     public String execute() throws Exception {
24         System.out.println("execute!!!");
25         return SUCCESS;
26     }
27 }

C.添加/显示错误

添加:    
   @Override public void validate() { if(student.getSname().trim().length()==0){ //向前台返回校验信息 this.addFieldError("student.sname", "学生名字不能为空!"); } }

  显示:

<body>
    <form action="${pageContext.request.contextPath }/check" method="post">
        <input type="text" name="student.sname"/>
        <!-- 获取后台返回的校验信息 -->
        <!-- 显示指定错误信息 -->
        <struts2怎么防止sql注入

struts2请求过程源代码分析

[struts2学习笔记] 第五节 编写struts2的action代码

Struts2 s2-032远程代码执行分析

S2-053:Apache Struts2远程代码执行漏洞(中危)

struts2 s2-062 ONGL远程代码执行