用来传递范围信息的上下文
在分布式环境中,范围跨多个组件。因此,在一个组件中创建的范围键必须被传递给其他组件,让这些组件可以使用键获取与这个范围相关联的对象。跨多个组件传递范围键的惟一方法是通过方法调用。为了帮助实现这种方式,需要用一个上下文对象存储范围信息。对于每个请求,创建一个上下文对象。这个对象存储当前请求范围、HTTP 会话范围和应用程序范围的键。然后,通过方法调用把这个对象传递给其他组件。当一个事务开始时,这个事务的范围键被添加到这个上下文对象中。当事务结束时,删除对应的范围键。清单 8 定义这个对象的 API.
清单 8. RequestContext API
| public interface RequestContext { /** * Get the scope key for the given scope type. * * @param scopeType * @return */ public Object getScopeKey(int scopeType); /** * Set the scope key for the given scope type. * * @param scopeType * @param scopeKey */ public void setScopeKey(int scopeType, Object scopeKey); } |
因为需要跨组件传递上下文对象,所以这个对象应该是可序列化的。
修改后的示例
现在可以用 JMS 和 RequestContext 修改前面的示例。除了创建请求范围键之外,在处理请求之前,MyRequestProcessor 需要获得应用程序范围键和会话范围键,并把它们存储在 RequestContext 对象中。在处理请求之后,它发送一个 JMS 消息,表示这个请求范围已经结束。清单 9 演示实现方法。
清单 9. 修改后的 RequestProcessor 类实现
| public class MyRequestProcessor extends RequestProcessor{ public void process(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { // assume when application started, the application id was generated // and stored in ServletContext with attribute name " appScopeKey" String appScopeKey = request.getServletContext().getAttribute("appScopeKey"); // session scope key String sessionScopeKey = request.getSession().getId(); // request scope key String requestScopeKey = UniqueIDGenerator.GetID(); RequestContext ctx = RequestContextFactory.getInstance(); ctx.setScopeKey(ScopeType.APPLICATION, appScopeKey); ctx.setScopeKey(ScopeType.HTTP_SESSION, sessionScopeKey); ctx.setScopeKey(ScopeType.REQUEST, requestScopeKey); // store the request context in request request.setAttribute("requestContext", ctx); try { // process the request super.process(request, response); } finally { // after the request is processed, release all objects bound // to this request scope TopicConnectionFactory tcf = (TopicConnectionFactory) ctx.lookup("ObjectManagerJMSConnection"); TopicConnection tc = tcf.createTopicConnection(); tc.start(); TopicSession ts = tc.createTopicSession(false, 1); Topic t = (Topic) ctx.lookup("ObjectManagerJMSTopic"); TopicPublisher tp = ts.createPublisher(t); TextMessage tm = ts.createTextMessage(); tm.setText(ScopeTypes.REQUEST + ":" + requestScopeKey); tp.publish(tm); } } } |
修改后的 AnyAction 类(清单 10)演示如何从 RequestContext 对象获取请求范围键并把 RequestContext 对象传递给 EJB 组件。
清单 10. 修改后的 Action 类实现
| public class AnyAction extends Action{ public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) { // ... RequestContext ctx = (RequestContext) request.getAttribute("requestContext"); // get the request scope key from the context object String scopeKey = ctx.getScopeKey(ScopeType.REQUEST); ObjectManager objMgr = ObjectManagerFactory.GetInstance(); obj = objMgr.getObject(obj.getClass(), ScopeTypes.REQUEST, scopeKey); // call methodY of sessionBeanX and pass the context object // as an parameter sessionBeanX.methodY(params, ctx); // ... } } |
清单 10 中的示例代码并不像 清单 5 那样从 HttpServletRequest 获取范围键,而是从 HttpServletRequest 获取 RequestContext 对象。然后,从 RequestContext 对象获取范围键。在调用 EJB 对象的远程或本地接口时,通过参数传递 RequestContext 对象。
我还添加了 SessionBeanX(清单 11),演示如何在 EJB 组件中使用 RequestContext 对象。
清单 11. SessionBean 类实现
| public class SessionBeanX implements SessionBean{ public void methodY (Object params, RequestContext ctx) { // ... // get the request scope key from the context object String scopeKey = ctx.getScopeKey(ScopeType.REQUEST); ObjectManager objMgr = ObjectManagerFactory.GetInstance(); obj = objMgr.getObject(obj.getClass(), ScopeTypes.REQUEST, scopeKey); // ... } } |
为了获取范围信息,SessionBeanX 类的每个方法必须有 RequestContext 参数。如 清单 10 所示,当 EJB 客户机调用远程或本地接口时,它必须传递 RequestContext 对象,这个对象包含关于当前范围的信息。然后,bean 方法中的 RequestContext 对象可以获得当前范围的键 —— 例如,通过 methodY()。
结束语
本文介绍了一个用于在 J2EE 应用程序中管理对象实例的框架。这个框架的主要优点是,它使用在 J2EE 中定义的事件触发机制自动地释放对象实例。这不仅使开发人员不必在代码中显式地释放对象,还可以减少内存泄漏的风险。本文的前半部分针对服务器端代码在单一 JVM 中运行的系统。后半部分针对服务器端代码在多个 JVM 中运行的环境。在本文中,我通过示例代码解释了概念。性能基准测试超出了本文的范围,将在以后的文章中讨论。


