天天看点

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保存的对象。这个时候如果用对话作用域,简单省事,提高了开发效率,代码也容易维护。