第一节 Struts2的OGNL
1.1 OGNL简介
- OGNL是Object Graphic Navigation Language(对象图导航语言)的缩写
- 它是一个单独的开源项目。 Struts2框架使用OGNL作为默认的表达式语言。
- OGNL是struts2整合的一个开源项目,所以在struts2中,要想使用OGNL表达式,必须要使用Struts2的标签库
- OGNL相当于EL表示式,从作用域取数据
1.2 OGNL的简单使用
- s:property类型于JSP的表达式,把value的值直接输出到页面
- jsp表达式:【<jsp:setProperty property="" name=""/>】
- value的值就是一个OGNL表达式,它不是一个字符串
- 如果想把value的值当字符串输出,加单引号即可
- index.jsp:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="/struts-tags" prefix="s"%>
<!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>
<%-- OGNL表达式
首先:要在jsp页面导入<%@ taglib uri="/struts-tags" prefix="s"%>
OGNL的作用:
1.取值,或者说输入数据到jsp页面
s:property的value不写单引号,代表要从作用域取值,如果写了单引号,则代表它就是一个字符串
2.调用方法
3.访问静态属性/方法
默认情况下,strut2把静态属性的访问禁止,如果要使用,在struts.xml中配置
<constant name="struts.ognl.allowStaticMethodAccess" value="true"></constant>
4.封装List数据,数组用花括号
<s:radio list="{
'男','女'}" name="gender" label="性别"/>
5.封装Map数据,用#{'key1':'value1','key2':'value2'}
--%>
访问对象方法:<br>
<s:property value="'java-struts2'"/>的长度为:
<s:property value="'java-struts2'.length()"/><br>
访问静态属性:<br>
int的最大值:<s:property value="@java.lang.Integer@MAX_VALUE"/><br>
访问静态方法:<br>
随机数:<s:property value="@java.lang.Math@random()"/><br>
封装lsit数据:<br>
<s:radio list="{
'男','女'}" name="gender" label="性别"></s:radio><br>
封装Map数据:<br>
<s:radio list="#{
'male':'男','female':'女'}" name="gender1" label="性别1"></s:radio><br>
</body>
</html>
效果:
1.3 OGNL的功能
- 访问对象方法
如上面index.jsp中字符串的length方法 - 访问静态属性(index.jsp)
- 访问静态方法(index.jsp)
默认OGNL是禁止静态方法访问的,在default.properties中有个struts.ognl.allowStaticMethodAccess=false,如想使用,需要在struts.xml文件中配置允许访问静态方法(index.jsp) - 封装list数据(index.jsp)
- 封装Map数据(index.jsp)
- struts.xml中写OGNL
- day05上中的文件下载案例就是一个OGNL表达式
- 修改DownloadAction.java
- 修改struts.xml
- 效果
- day05上中的文件下载案例就是一个OGNL表达式
第二节 contextMap
2.1 动作类的生命周期
- struts使用contextMap存数据
- 动作类是多例的,每次动作访问,动作类都会实例化,所以是线程安全的。
- 与Struts1的区别是,struts1的动作类是单例的。
例:
- 写一个TestAction.java
package com.it.web.action;
import com.opensymphony.xwork2.ActionSupport;
public class TestAction extends ActionSupport{
@Override
public String execute() throws Exception {
System.out.println(this);
//org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter
return SUCCESS;
}
}
- struts.xml
<package name="p1" extends="struts-default">
<action name="test" class="com.it.web.action.TestAction">
</action>
</package>
- 多次访问:http://localhost:8080/day05_struts2_demo2-ognl/test
发现每次地址都不同,避免多线程访问同一个变量,所以是线程安全的。
2.2 请求数据的存放
- 每次请求时,都会产生一些请求数据,这些数据存放到哪里去了?
- 在每次动作执行前,核心控制器StrutsPrepareAndExecuteFilter都会创建一个ActionContext和ValueStack对象,且每次动作访问都会创建。
- 这两个对象存储了整个动作访问期间用到的数据。
- 并且把数据绑定到了线程局部变量(ThreadLocal)上了,所以是线程安全的。
2.3 contextMap存储数据
- Struts2的官方文档对contextMap的说明:
contextMap(=ActionContext)中存放的主要内容 | ||
Key | Value | 说明 |
value stack (root) | java.util.List | 没有root这个key。它是一个list。 |
application | java.util.Map<String,Object> | ServletContext中的所有属性 |
session | java.util.Map<String,Object> | HttpSession中的所有属性。 |
request | java.util.Map<String,Object> | ServletRequest中的所有属性。 |
parameters | java.util.Map | 参数 |
attr | java.util.Map | 把页面、请求、会话、应用范围内的所有属性放到一起。 |
root栈:继承了List接口,又称之为对象栈
map栈:实现了Map接口,又可以称之为上下文栈(context)
除了value stack之外,全是map,而contextMap也是一个map。
其实就是Map中又封装的Map。(很像dbutils中KeyedHandler封装数据的结构,只是封装数据的结构)
- 使用s:debug查看contextMap的数据
2.4 测试存储数据
- test.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="/struts-tags" prefix="s"%>
<!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>
<%session.setAttribute("username", "shu"); %>
<s:debug></s:debug>
</body>
</html>
这里要先存数据,再debug显示,不然不显示
- struts.xml
<package name="p1" extends="struts-default">
<action name="test" class="com.it.web.action.TestAction">
<result>/test.jsp</result>
</action>
</package>
- 效果
第三节 contextMap中的数据操作
3.1 存数据
利用ActionContext存数据
- Test1Action.java
package com.it.web.action;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
public class Test1Action extends ActionSupport{
@Override
public String execute() throws Exception {
// TODO Auto-generated method stub
//往cotextMap存数据
//1.获取ActionContext
ActionContext contextMap = ActionContext.getContext();
//2.往cotextMap存数据
contextMap.put("username", "shu01");
contextMap.put("password", "shu01");
//3.往cotextMap的session存数据
Map<String, Object> sessionMap = contextMap.getSession();
//也可以通过 HttpSession httpSession = ServletActionContext.getRequest().getSession();
sessionMap.put("username", "shu02");
sessionMap.put("password", "shu02");
//4.往cotextMap的request存数据
HttpServletRequest request = ServletActionContext.getRequest();
request.setAttribute("username", "shu03");
//5.往cotextMap的application存数据
//application = ServletContext 【ServletActionContext.getServletContext()】
Map<String, Object> applicationMap = contextMap.getApplication();
applicationMap.put("username", "shu04");
return SUCCESS;
}
}
- struts.xml
<!-- 开发模式 -->
<constant name="struts.devMode" value="true"></constant>
<!-- 配置允许OGNL表达式调用静态方法、属性 -->
<constant name="struts.ognl.allowStaticMethodAccess" value="true"></constant>
<package name="p1" extends="struts-default">
<action name="test1" class="com.it.web.action.Test1Action">
<result>/test1.jsp</result>
</action>
</package>
- test1.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="/struts-tags" prefix="s"%>
<!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>Test1</title>
</head>
<body>
<s:debug></s:debug>
</body>
</html>
- 效果
1.往cotextMap存数据
2.往cotextMap的session存数据
3.往cotextMap的request存数据
4.往cotextMap的application存数据
利用ValueStack存数据
三种获取ValueStack的方法
- 先提供一个User.java,username、password的getter、setter方法、有参构造
- Test2Action.java
package com.it.web.action;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.util.ValueStack;
public class Test2Action extends ActionSupport{
@Override
public String execute() throws Exception {
// TODO Auto-generated method stub
//往ValueStack【值栈】存数据
//1.第一种获取“值栈”的方法【推荐使用,方便简单】
//获取“值栈”对象的引用
ValueStack vs1 = ActionContext.getContext().getValueStack();
//通过值栈存储信息
vs1.push(new User("shu01", "123"));
//2.第二种获取“值栈”的方法
@SuppressWarnings("unchecked")
Map<String,Object> requestMap = (Map<String,Object>) ActionContext.getContext().get("request");
ValueStack vs2 = (ValueStack) requestMap.get("struts.valueStack");
//3.第三种获取“值栈”的方法
ValueStack vs3 = (ValueStack) ServletActionContext.getRequest().getAttribute("struts.valueStack");
System.out.println(vs1);
System.out.println(vs2);
System.out.println(vs3);
return SUCCESS;
}
}
- struts.xml在上面的p1包下添加
<action name="test2" class="com.it.web.action.Test2Action">
<result>/test2.jsp</result>
</action>
- 效果
3.2 取数据
- 用Struts2的标签(OGNL表达式)在JSP上(用的最多)
使用s:property取数据
- 取contextMap中的数据,需使用#+key
- 取contextMap里面ValueStack中对象的属性:直接写属性名key
- Test3Action.java
package com.it.web.action;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.util.ValueStack;
public class Test3Action extends ActionSupport{
private String username = "shu action属性";
//如果在Action的属性中有get方法,它会把这个数据存在ValueStack
public String getUsername() {
return username;
}
@Override
public String execute() throws Exception {
// TODO Auto-generated method stub
//1.往ValueStack【值栈】存数据
ValueStack vs1 = ActionContext.getContext().getValueStack();
vs1.push(new User("shu05", "123"));
//获取ActionContext
ActionContext contextMap = ActionContext.getContext();
//2.往cotextMap存数据
contextMap.put("username", "shu01");
//3.往cotextMap的session存数据
Map<String, Object> sessionMap = contextMap.getSession();
sessionMap.put("username", "shu02");
//4.往cotextMap的request存数据
HttpServletRequest request = ServletActionContext.getRequest();
request.setAttribute("username", "shu03");
//5.往cotextMap的application存数据
Map<String, Object> applicationMap = contextMap.getApplication();
applicationMap.put("username", "shu04");
return SUCCESS;
}
}
- test3.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="/struts-tags" prefix="s"%>
<!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>Test3</title>
</head>
<body>
<%-- 取值 <s:property value=""/>
value写的是OGNL表达
如果要取contextMap,写#+key名字
如果是取ValueStack,直接key名字 --%>
<!-- 如果遇有对象属性重名,可以通过OGNL表达式,选择查找的起始位置-->
取ValueStack的username:<s:property value="[0].username"/><br>
取ValueStack的username:<s:property value="[1].username"/><br>
<!-- 这里的username、password不是字符串而是OGNL表达式 -->
取ValueStack的username:<s:property value="username"/><br>
取ValueStack的password:<s:property value="password"/><br>
取contextMap的:<s:property value="#username"/><br>
取contextMap的session的:<s:property value="#session.username"/><br>
取contextMap的request的:<s:property value="#request.username"/><br>
取contextMap的application的:<s:property value="#application.username"/><br>
<!-- 当s:property不给定value属性时,默认取栈顶对象。 -->
栈顶<s:property/>
<s:debug></s:debug>
</body>
</html>
- struts.xml添加
<action name="test3" class="com.it.web.action.Test3Action">
<result>/test3.jsp</result>
</action>
- 效果
- 解释
- private String username = “shu action属性”;需要提供get方法,ValueStack中才有它的值
2.在jsp页面取值时,可以通过索引取,也可以注释vs1.push(new User(“shu05”, “123”));,这时username就为shu action属性,如果不注释取的是User里的shu05
注释vs1.push(new User(“shu05”, “123”));后:
3.当s:property不给定value属性时,默认取栈顶对象。
4.存储2个username,后面的username为什么不替换前一个?
它们所属的Action不同,一个是vs1.push(new User(“shu05”, “123”));属于User,一个是private String username = “shu action属性”;属于Test3Action
- private String username = “shu action属性”;需要提供get方法,ValueStack中才有它的值
3.3 valueStack的其它方法
- 先提供一个User.java包含username、password的getter、setter、有参构造
put方法:往栈顶添加元素
- demo01.jsp导入struts标签库,配置debug
<%@ taglib uri="/struts-tags" prefix="s"%>
<s:debug></s:debug>
- Demo01Action.java
package com.it.web.action;
import com.it.model.User;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.util.ValueStack;
public class Demo01Action extends ActionSupport{
@Override
public String execute() throws Exception {
// TODO Auto-generated method stub
//往值栈存数据
ValueStack vs = ActionContext.getContext().getValueStack();
//1.push方法:往栈顶添加数据
vs.push(new User("shu01", "123"));
vs.push(new User("shu02", "124"));
return SUCCESS;
}
}
- struts.xml常规配置
<constant name="struts.devMode" value="true"></constant>
<package name="p1" extends="struts-default">
<action name="demo01" class="com.it.web.action.Demo01Action">
<result>/demo01.jsp</result>
</action>
</package>
- 效果:它不会替换,会压栈,先进后出
setValue方法:更改栈顶元素的值
- 修改Demo01Action.java的代码,注释一个push,留一个做栈顶,其它不变
//1.push方法:往栈顶添加数据
vs.push(new User("shu01", "123"));
//vs.push(new User("shu02", "124"));
//2.setValue方法:更改栈顶的username的值
vs.setValue("username", "shu03");
- 效果
- 注意,如果栈里没有setValue所修改的key,会报错
//栈中没有key为username2,会报错
vs.setValue("username2", "shu03");
//Error setting expression 'username2' with value 'shu03' - [unknown location]
4. 其它
注释put,添加一个private String username = “shu”;此时User不再是栈顶,Demo01Action中的username为栈顶
private String username = "shu";
public String getUsername() {
return username;
}
//1.push方法:往栈顶添加数据
//vs.push(new User("shu01", "123"));
//vs.push(new User("shu02", "124"));
再使用setValue修改栈顶值,还需要提供userneme的setter方法,否则报错
//2.setValue方法:更改栈顶的username的值
vs.setValue("username", "shu03");
设置setter后:
public class Demo01Action extends ActionSupport{
private String username = "shu";
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
@Override
public String execute() throws Exception {
// TODO Auto-generated method stub
//往值栈存数据
ValueStack vs = ActionContext.getContext().getValueStack();
//1.push方法:往栈顶添加数据
//vs.push(new User("shu01", "123"));
//vs.push(new User("shu02", "124"));
//2.setValue方法:更改栈顶的username的值
vs.setValue("username", "shu03");
return SUCCESS;
}
}
setValue方法:往contextmap存数据
- 继续在Demo01Action.java中添加以下代码
//3.setValue往contextmap存数据
vs.setValue("#username3", "shu03");
- 效果
set方法:往值栈栈顶存map数据
- Demo01Action.java
package com.it.web.action;
import com.it.model.User;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.util.ValueStack;
public class Demo01Action extends ActionSupport{
private String username = "shu";
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
@Override
public String execute() throws Exception {
// TODO Auto-generated method stub
//往值栈存数据
ValueStack vs = ActionContext.getContext().getValueStack();
//1.push方法:往栈顶添加数据
vs.push(new User("shu01", "123"));
//vs.push(new User("shu02", "124"));
//2.setValue方法:更改栈顶的username的值
vs.setValue("username", "shu03");
/**
* 如果栈里没有这key会报错,vs.setValue("username2", "shu03");
* Error setting expression 'username2' with value 'shu03' - [unknown location]
*/
//3.往contextmap存数据
vs.setValue("#username3", "shu03");
//4.set方法 :往值栈存map数据
vs.set("user", new User("shu04", "125"));
return SUCCESS;
}
}
- 数据未显示,在jsp页面使用findValue方法取出栈顶map元素的内容
findValue方法:取出栈顶map元素的内容
- 在上面的基础上使用findValue方法修改demo01.jsp
<%@page import="com.opensymphony.xwork2.util.ValueStack"%>
<%@page import="com.opensymphony.xwork2.ActionContext"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="/struts-tags" prefix="s"%>
<!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>Demo01.jsp</title>
</head>
<body>
<!-- 用来看contextMap数据 -->
获取栈顶map的数据
<%
ValueStack vs = ActionContext.getContext().getValueStack();
String value = (String)vs.findValue("user.username");
out.write(value);
%>
栈顶:<s:property/>
<s:debug></s:debug>
</body>
</html>
- 效果,成功取到了map中存的 shu04
- 简便方法,推荐使用第二种,简单方便
获取栈顶map的数据方法1:
<%
ValueStack vs = ActionContext.getContext().getValueStack();
String value = (String)vs.findValue("user.username");
out.write(value);
%>
<br>
获取栈顶map的数据方法2:<s:property value="user.username"/><br>
栈顶:<s:property/>
<s:debug></s:debug>
3.4 Struts中对EL取值顺序的改变
默认情况下EL查找顺序
- 表达式取值必须是servlet四大作用域,由小到大排列为:
- pageContext【page】 -> request -> session -> servletContext【application】
在Struts中EL表达式的查找顺序的改变
- page –> request -> ValueStack(根中)-> contextMap -> session -> application
案例
- Demo02Action.java
package com.it.web.action;
import org.apache.struts2.ServletActionContext;
import org.apache.struts2.dispatcher.StrutsRequestWrapper;
import com.it.model.User;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.util.ValueStack;
public class Demo02Action extends ActionSupport{
@Override
public String execute() throws Exception {
//StrutsRequestWrapper srw;//源码
// TODO Auto-generated method stub
//使用struts后EL表达式的取数据顺序
/**
* Servlet/JSP:存数据 page -> request -> session -> application
* Struts:存数据contextMap(Actioncontext),ValueStack
*
*/
//1.往contextMap(Actioncontext)
ActionContext.getContext().put("username", "shu-contextMap");
//2.往值栈存数据
ValueStack vs = ActionContext.getContext().getValueStack();
vs.push(new User("shu-valueStack", null));
//3.往request
ServletActionContext.getRequest().setAttribute("username", "shu-request");
//4.往session
ServletActionContext.getRequest().getSession().setAttribute("username", "shu-session");
return SUCCESS;
}
}
- struts.xml中添加
<action name="demo02" class="com.it.web.action.Demo02Action">
<result>/demo02.jsp</result>
</action>
- demo02.jsp
<%@page import="com.opensymphony.xwork2.util.ValueStack"%>
<%@page import="com.opensymphony.xwork2.ActionContext"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="/struts-tags" prefix="s"%>
<!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>Demo01.jsp</title>
</head>
<body>
<!-- 使用EL表达式来取ActionContext,ValueStack数据
研究它的取值顺序:page -> request -> valueStack -> contextMap -> session -> applaction
以前用Servlet的时候,在jsp中取数据从request中取
如果使用struts2,在jsp中取数据,一般从ValueStack中取
-->
${username}
<s:debug></s:debug>
</body>
</html>
- 运行结果
注释request后
再注释valueStack后
再注释contextMap后