对话作用域
对话作用域需要用到一个很关键的接口,javax.enterprise.context.Conversation。
默认情况下,对话作用域是跟RequestScope一样的,也就是来一个HTTP请求,就新建一个对象。请求结束,对象也终结了。
那么要如何才能传给下一个请求呢?
需要做两件事情
1 将对话设置为开始,也就是对话状态由临时对话设置为长时间对话。
这个需要调用javax.enterprise.context.Conversatio.begin()方法,这个方法还可以提供一个对话ID,不过容器会自动生成ID,默认是递增的,由1开始往上加。
2 在跳转下一个请求时加上请求参数cid,可以是get参数或者是post参数。加上了参数之后,下一个请求就不会新建一个对话了。而且根据ID去对话上下文查找对话。
那么如何结束对话呢?虽然说不带cid参数,对话就不会传递过去,但是对话不结束就会一直占据系统资源,造成内存的浪费。结束对话使用javax.enterprise.context.Conversation.end方法。
我们来写一个简单的demo实验一下。这个demo是这样的,有两个URL,在第一个URL生成的页面中提供一个链接跳到第二个URL,第二个URL关闭对话。
那么我们来写代码吧。
首先是接口。
package cdiscope;
public interface ConversationService {
void beginConversation();
void endConversation();
}
其次实现类
package cdiscope;
import java.io.Serializable;
import javax.enterprise.context.Conversation;
import javax.enterprise.context.ConversationScoped;
import javax.inject.Inject;
import javax.inject.Named;
@Named
@ConversationScoped()
public class ConversationServiceBean implements ConversationService, Serializable {
private static final long serialVersionUID = 1L;
@Inject
private Conversation conversation;
@Override
public void beginConversation() {
if (conversation.isTransient()) {
conversation.begin();
}
}
@Override
public void endConversation() {
if (!conversation.isTransient()) {
conversation.end();
}
}
}
然后是两个servlet。
package cdiscope;
import java.io.IOException;
import javax.enterprise.context.Conversation;
import javax.inject.Inject;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/conversation1.html")
public class Conversation1Servlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Inject
private ConversationServiceBean conversationService;
@Inject()
private Conversation conversation;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
conversationService.beginConversation();
System.out.println("In servlet " + conversation);
resp.getWriter().append("<html><body>The service is " + conversationService)
.append(".<br/><a href=\"conversation2.html?cid=").append(conversation.getId())
.append("\">Next</a></body></html>");
}
}
可以看到这个servlet中将cid参数传递给了下一个servlet。
第二个servlet代码
package cdiscope;
import java.io.IOException;
import javax.inject.Inject;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/conversation2.html")
public class Conversation2Servlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Inject
private ConversationServiceBean conversationService;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
conversationService.endConversation();
resp.getWriter().append("<html><body>The service is " + conversationService)
.append(".<br/><a href=\"conversation1.html\">Back</a></body></html>");
}
}
可以看到这个servlet把对话结束了。
那么我们运行一下看看结果:
第一次请求
然后跳到第二个请求,可以看到对象的内存地址并没有改变。
再点击Back跳回来,由于已经结束了对话,所以内存地址已经变了。
可以这样理解,对话作用域范围介于session与request之间,那么它的实用价值在哪呢?
比如我们做一个复杂的注册应用。
注册分三步或者四步甚至更多,保存在request里肯定不科学,如果保存在页面的隐藏域也并不太好。因为如果我要后退修改前一步的操作呢?势必要写相当复杂的代码。
如果存在session里,到最后要一个个地去清除session保存的对象。这个时候如果用对话作用域,简单省事,提高了开发效率,代码也容易维护。