對話作用域
對話作用域需要用到一個很關鍵的接口,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儲存的對象。這個時候如果用對話作用域,簡單省事,提高了開發效率,代碼也容易維護。