主要来学习一下struts2的数据存储
contextMap
动作类的生命周期
动作类是多例的,每一次动过访问,动过类都会实例化,所以是线程安全的。
struts2中是怎么存储数据的了,其实在每次请求到来时,核心控制器StrutsPrepareAndExecuteFilter都会创建一个ActionContext和ValueStack,并且每次动作访问都会创建,我们可以下StrutsPrepareAndExecuteFilter类中的方法:
-
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
-
-
HttpServletRequest request = (HttpServletRequest) req;
-
HttpServletResponse response = (HttpServletResponse) res;
-
-
try {
-
prepare.setEncodingAndLocale(request, response);
-
prepare.createActionContext(request, response);
-
prepare.assignDispatcherToThread();
-
if (excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) {
-
chain.doFilter(request, response);
-
} else {
-
request = prepare.wrapRequest(request);
-
ActionMapping mapping = prepare.findActionMapping(request, response, true);
-
if (mapping == null) {
-
boolean handled = execute.executeStaticResourceRequest(request, response);
-
if (!handled) {
-
chain.doFilter(request, response);
-
}
-
} else {
-
execute.executeAction(request, response, mapping);
-
}
-
}
-
} finally {
-
prepare.cleanupRequest(request);
-
}
-
}
可以看到它有执行
-
prepare.createActionContext(request, response);
再去看这个方法:
-
public ActionContext createActionContext(HttpServletRequest request, HttpServletResponse response) {
-
ActionContext ctx;
-
Integer counter = 1;
-
Integer oldCounter = (Integer) request.getAttribute(CLEANUP_RECURSION_COUNTER);
-
if (oldCounter != null) {
-
counter = oldCounter + 1;
-
}
-
-
ActionContext oldContext = ActionContext.getContext();
-
if (oldContext != null) {
-
// detected existing context, so we are probably in a forward
-
ctx = new ActionContext(new HashMap<String, Object>(oldContext.getContextMap()));
-
} else {
-
ValueStack stack = dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack();
-
stack.getContext().putAll(dispatcher.createContextMap(request, response, null, servletContext));
-
ctx = new ActionContext(stack.getContext());
-
}
-
request.setAttribute(CLEANUP_RECURSION_COUNTER, counter);
-
ActionContext.setContext(ctx);
-
return ctx;
-
}
可以看到,它有创建了ValueStack和ActionContext,并且从下面ActionContext类可以看到,其创建是关联到当前线程的,所以是安全的
-
public class ActionContext implements Serializable {
-
-
static ThreadLocal<ActionContext> actionContext = new ThreadLocal<ActionContext>();
ContextMap存储数据
ContextMap中存储了很多的数据,但是我们经常用到的数据就是下面的这些部分

我们怎样查看到ValueStack和ContextMap中存储的数据呢,Struts2提供了<s:debug标签,它可以让我们查看到ContextMap和ValueStack中的数据。


可以看到ValueStack是一个list,ContextMap是一个Map型的数据结构,它是以键值对的形式的存储数据的。
在ContextMap中的键有些比较长,有些短,这些短就是开发者所使用的,长值是框架所使用的,基本要使用的就是上面表格中列出的。
例如:我们在session中存储一条数据
-
<html>
-
<head>
-
<title>用户注册,使用的是struts2的标签</title>
-
<s:head></s:head>
-
</head>
-
<body>
-
<%session.setAttribute("name", "张三"); %>
-
<s:debug></s:debug>
-
</body>
-
</html>
ActionContext和ValueStack存储数据
1.向ActonContext存数据
-
public String saveUser(){
-
//获取ContextMap
-
ActionContext actionContext = ActionContext.getContext();
-
actionContext.put("name", "contextMap");
-
//向HttpSession存储数据
-
//得到Session
-
Map<String, Object> session = actionContext.getSession();
-
session.put("name", "actionContextSession");
-
//原始Session存储对象
-
HttpSession httpSession = ServletActionContext.getRequest().getSession();
-
httpSession.setAttribute("age", "21");
-
return SUCCESS;
-
}

从结果可以看到,不管是ContextMap得到的session还是HttpSession得到的session,其数据都存储在相同的地方.
2.向ValueStack存储数据
a.向ValueStack存普通数据
存普通数据有三种方式:
第一种:直接使用set方法
第二种:使用push方法
第三种:生成get方法
-
public String saveUser(){
-
//获取ContextMap
-
ActionContext actionContext = ActionContext.getContext();
-
//获取值栈对象
-
ValueStack valueStack =actionContext.getValueStack();
-
//使用set方法存储数据
-
valueStack.set("name", "valueStack");
-
//使用push方法存储数据
-
valueStack.push("dadadada");
-
return SUCCESS;
-
}
生成get方法存储数据
-
public int getNum() {
-
return num;
-
}

b.向值栈存放对象
实现步骤
第一步定义对象变量
第二步生成变量的get方法
第三步在执行的方法里面向对象中设置值
-
private User user=new User();
-
public User getUser() {
-
return user;
-
}
-
public String saveUser(){;
-
user.setName("user");
-
user.setGender("gender");
-
return SUCCESS;
-
}
c.存储list集合
存储集合和存储对象是一样的步骤
-
private List<String> listStr = new ArrayList<String>();
-
public String saveUser(){
-
//获取ContextMap
-
ActionContext actionContext = ActionContext.getContext();
-
//获取值栈对象
-
ValueStack valueStack =actionContext.getValueStack();
-
listStr.add("A");
-
listStr.add("B");
-
listStr.add("C");
-
return SUCCESS;
-
}
-
public List<String> getListStr() {
-
return listStr;
-
}
取数据:在jsp中使用struts2的ONGL表达式
在Map中取数据:map中取数据ongl使用#key
-
<s:property value="#name"/>
-
<s:property value="#session.name"/>


取ValueStack中的数据,ValueStack是一个List,ONGL取list中的数据时直接写属性名
-
<!--这是ONGL表达式,不是字符串 -->
-
<s:property value="name"/>
因为在ValueStack中从上往下找,第一个属性为name就是存储的valueStack

如果想要获取下一个属性名为name的值
-
<s:property value="[1].name"/>
ValueStack的其它方法
a.setValue
-
/**
-
* @see com.opensymphony.xwork2.util.ValueStack#setValue(java.lang.String, java.lang.Object)
-
*/
-
public void setValue(String expr, Object value) {
-
setValue(expr, value, devMode);
-
}
可以看到,setValue的name是一个ONGL的表达式,所以,如果expr不加#是存储到ValueStack,否则存储到MAP中。
-
//有#,表示存储到contextMap中,否则存储到ValueStack中
-
valueStack.setValue("#address", "南山");
-
valueStack.setValue("address", "南山");
b.set方法
-
//如果栈顶是一个map,那么就直接把数据塞到map中,否则,创建一个MAP,然后再把数据塞到MAP中。
-
//使用set方法存储数据
-
valueStack.set("name", "valueStack");
c.findValue
在JSP中调用的是就是findValue方法,该方法先是在ValueStack中查找,然后再ContextMap中查找。
Struts2对EL表达式的改变
例1:向Reques和ValueStack中存储相同属性名的数据
-
public class HelloAction extends ActionSupport implements ModelDriven<User>{
-
-
private User user=new User();
-
private String name="ValueStack中的name";
-
@Override
-
public User getModel() {
-
// TODO Auto-generated method stub
-
return user;
-
}
-
-
public String saveUser(){
-
ServletRequest sq=ServletActionContext.getRequest();
-
sq.setAttribute("name", "request中的name");
-
return SUCCESS;
-
}
-
public String getName() {
-
return name;
-
}
-
}
使用EL表达式查看name的值

从结果可以看到,取到了值
例2:使用application存数据
-
public class HelloAction extends ActionSupport{
-
-
private String name="ValueStack中的name";
-
public String saveUser(){
-
ServletContext application=ServletActionContext.getServletContext();
-
application.setAttribute("name", "application中的name");
-
return SUCCESS;
-
}
-
-
public String getName() {
-
return name;
-
}
-
}
EL表达式结果值:

EL表达式是取的域中的数据,怎么没有取到了.原因是Struts2有对EL表达式进行了改写

Struts2中EL查找顺序改变总结:
原始的EL表达式查找顺序: page Scope————>request Scope————>sessionScope————>application
Scope
现在的OGNL(改写后的EL表达式)表达式:page Scope————>request Scope————>valueStack(根中)————>contextMap————>sessionScope————>application
Scope
Struts2一些通用标签的使用
a.iterator标签
一般就是迭代list中的数据显示在页面
-
<!--
-
value:ONGL表达式
-
var:是一个字符串,如果指定了var属性,则会把框架当前的元素,以var为key,存储在contextMap中
-
如果没有指定,则会把当前值存储在ValueStack中.
-
-->
-
<table>
-
<thead>
-
<tr><td>序号</td><td>姓名</td></tr>
-
</thead>
-
<tbody>
-
</tbody>
-
<s:iterator value="listUser" var="s" status="vs">
-
<tr> <td><s:property value="#vs.count"/>
-
<!--不管是使用ONGL表达式,还是EL表达式都可以取到值,只是注意,有var的时候是存储在ContextMap中 -->
-
</td><td><s:property value="#s.name"/></br>${s.name}</td></tr>
-
</s:iterator>
-
</table>
Struts2中#,$,%符号的使用
a.# 主要有两种用法,第一种是,取ContextMap中的数据,第二个是使用ONGL表达式创建MAP。
b、在xml配置文件中,编写OGNL表达式时使用,例如文件下载时,文件名编码。
struts.xml——>${@Java.NET.URLEncoder.encode(filename)}
c.%
在struts2中,有些标签的value属性取值就是一个OGNL表达式,例如<s:property value="OGNL Expression" /> 还有一部分标签,value属性的取值就是普通字 符串,例如<s:textfield value="username"/>,如果想把一个普通的字符串强制看成时OGNL,就需要使用%{}把字符串套起来。
例如<s:textfield value="%{username}"/>。当然在<s:property value="%{OGNL Expression}"/>也可以使用,但不会这么用。
常用的标签 url a
-
<!--s:url 标签是创建一个地址
-
alue:表示一个字符串,不是ONGL表达式,就是输出一个字符串
-
-->
-
lt;s:url value="name"></s:url>
结果:

-
<!--s:url 标签是创建一个地址
-
value:表示一个字符串,不是ONGL表达式,就是输出一个字符串
-
action:输出的是action的地址,相当于${pageContext.requeat.contextPath}/action1.action
-
-->
-
<s:url action="action1"></s:url>
-
<%-- s:url 标签是创建一个地址
-
value:表示一个字符串,不是ONGL表达式,就是输出一个字符串
-
action:输出的是action的地址,相当于${pageContext.requeat.contextPath}/action1.action
-
var属性:会把action属性的值存储到ContextMap中
-
在s:url中使用s:param标签,则是给url表达式添加参数
-
--%>
-
<s:url action="action1" var="url">
-
<s:param name="username" value="'test'"></s:param>
-
</s:url>
-
<s:debug/>
可以看到在request中有url为key存储的数据

-
<!--s:a标签和s:url一样 -->
-
<s:a action="action1" var="url">
-
<s:param name="username" value="'test'"></s:param>
-
</s:a>
这样,我们可以把该url的值给a标签
-
<a href="<s:property value='#url' />">请点击</a>
-