天天看點

CDI技術第三步,對話作用域

對話作用域

對話作用域需要用到一個很關鍵的接口,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把對話結束了。

那麼我們運作一下看看結果:

第一次請求

CDI技術第三步,對話作用域

然後跳到第二個請求,可以看到對象的記憶體位址并沒有改變。

CDI技術第三步,對話作用域

再點選Back跳回來,由于已經結束了對話,是以記憶體位址已經變了。

CDI技術第三步,對話作用域

可以這樣了解,對話作用域範圍介于session與request之間,那麼它的實用價值在哪呢?

比如我們做一個複雜的注冊應用。

注冊分三步或者四步甚至更多,儲存在request裡肯定不科學,如果儲存在頁面的隐藏域也并不太好。因為如果我要後退修改前一步的操作呢?勢必要寫相當複雜的代碼。

如果存在session裡,到最後要一個個地去清除session儲存的對象。這個時候如果用對話作用域,簡單省事,提高了開發效率,代碼也容易維護。