主要来学习一下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 标签是创建一个地址
value:表示一个字符串,不是ONGL表达式,就是输出一个字符串
-->
<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>