Topic: EJB 最佳实践:动态委派

  Print this page

1.EJB 最佳实践:动态委派 Copy to clipboard
Posted by: palatum
Posted on: 2003-03-17 18:55

EJB 最佳实践:动态委派

用 Java 反射构建更通用的业务委派
级别:中级


Brett McLaughlin(brett@oreilly.com)
作家兼编辑,O'Reilly and Associates
2003 年 3 月

尽管业务委派类确实给您的企业 Java 设计带来了激动人心的新灵活性,但为您应用程序中的每个会话 bean 都编码一个业务委派还是太麻烦了。在 EJB 最佳实践系列的这篇文章里,Brett McLaughlin 向您展示了如何创建业务委派类的更通用的版本:动态委派。
在上一篇技巧文章中,我们讨论了如何用业务委派类(请不要与业务接口(Business Interface)模式相混淆)来访问您的 EJB 组件。通过在客户机代码和 EJB 代码之间插入业务委派类,我们可以将应用程序的 Web 层与 EJB 语义和业务逻辑隔离开来。

研究这类设计的一种方法是看它有多通用。先从一个应用程序入手,该应用程序中的业务逻辑和技术函数是紧密地交织在一起的,我们已经逐步分离出应用程序的不同层,并使用不同的技术来降低它们的相互依赖。在这样做时,您应该会发现:应用程序底层结构越通用,则随着时间的推移,它就会越健壮且可维护性越好。

在这篇技巧文章中,我们将继续使用通用设计的思想。我们将从研究当前业务委派实现的限制入手,然后我将向您展示如何通过创建更通用的(因而不那么呆板)业务委派类实现来克服这些限制。

业务委派类:复习
回顾一下我们上个月讨论的 Library bean 接口的业务委派类。

LibraryDelegate 类的大部分代码只是复制了原始 Library bean 的方法。LibraryDelegate 添加了 init()、destroy() 和构造器方法,然后用这些方法将任务委派给 Library bean。在这样做时,委派充当 Web 层和企业 bean 之间的缓冲区。这里是原始 bean 的业务接口。

方法的繁殖
除非您考虑到多个会话 bean 有 10 个、20 个或更多方法,否则这种方法的问题并不明显。实际上,找到拥有 50 个或更多方法的会话 bean 并不罕见!因为 bean 的业务接口必须包括该 bean 的所有方法,所以业务委派类也将这样做。那会使代码过于庞大,并很容易出错。

您的输入是否太快了?
在使用 EJB 组件时,我们经常跨越远程接口、业务接口、实现类和现在的业务委派类来复制许多方法。我们中的许多人喜欢在编辑器窗口和 IDE 之间剪切和粘贴方法,而不是手工输入它们,但请注意:按 Option+V 或 Control+V 会象手工输入方法一样容易出错 — 您添加的方法越多,出错的可能性就越大。通过仔细检验您是否正确地输入了方法,以及是否按预期剪切和粘贴了它们,最终可以使您避免许多麻烦。
除了庞大的代码之外,我们还必须考虑变化因素。因为 Delegate 类必须复制 bean 的所有方法,并且随着时间推移,bean 不可避免地会发生变化,您会发现需要花费很多时间来将新的方法添加到 Delegate,更别提重新编译了,有可能还要测试新代码。

就其本身而言,这看起来似乎不是很严重的问题。但假如我们开始使用业务委派类来从技术基础结构(在本文是指 EJB 组件)中抽象业务和表示逻辑呢。如果更改远程接口需要对业务委派进行更改,那么,实际上,我们的业务委派仍然与底层组件联系在一起。

我们需要更好的方法,您说是不是。确实有更好的方法。

动态委派
解决方案是使用动态委派,而它又使用 Java 反射(reflection)。您可以使委派动态地调用目标 EJB 组件的远程接口上的方法(通过 Java Reflection API),而不必将每个业务方法硬编码到委派中。这样允许彻底消除来自远程接口的耦合,因为,为 bean 的业务或远程接口添加方法时不需要在业务委派中进行相应更改。使用动态委派还使得更改您的技术基础结构更为容易。从远程接口和 EJB 技术迁移到另一种技术(如 Java Data Objects,JDO)只需要更改委派的 init() 方法。所有其它方法调用将继续通过 bean 的接口进行委派,并且可以继续使用无需进一步更改。清单 1 显示了 Library 业务委派的动态版本:

清单 1. Library bean 的业务委派
package com.ibm.library;

import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;
import java.rmi.RemoteException;
import java.util.HashMap;
import java.util.Map;
import javax.ejb.CreateException;
import javax.naming.NamingException;

public class LibraryDelegate implements ILibrary {
private ILibrary library;
private Map availableMethods;

public LibraryDelegate() {
init();
}

public void init() {
// Look up and obtain our session bean
try {
LibraryHome libraryHome =
(LibraryHome)EJBHomeFactory.getInstance().lookup(
"java:comp/env/ejb/LibraryHome", LibraryHome.class);
library = libraryHome.create();

// Get the methods available for use in proxying
availableMethods = new HashMap();
Method[] methods = ILibrary.class.getMethods();
for (int i=0; i<methods.length; i++) {
availableMethods.put(methods[i].getName(),
methods[i]);
}
} catch (NamingException e) {
throw new RuntimeException(e);
} catch (CreateException e) {
throw new RuntimeException(e);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}

// All the hard-coded methods are removed
public Object
invoke(Object proxy, Method method, Object[] args)
throws Throwable{

try {
// See if this is init() or destroy()
if (method.getName().equals("init")) {
init();
return null;
} else if (method.getName().equals("destroy")) {
destroy();
return null;
} else {
Method method =
(Method)availableMethods.get(method.getName());

// See if we found anything
if (method != null) {
return method.invoke(library, args);
} else {
throw new
NoSuchMethodException("The Library does not " +
"support the " + method.getName() +" method.");
}
}
} catch (InvocationTargetException e) {
// We don't support throwing RuntimeExceptions from EJBs
// directly
if (e.getTargetException() instanceof RemoteException) {
throw new RuntimeException(e);
} else {
throw e.getTargetException();
}
}
}

public void destroy() {
// In this case, do nothing
}
}



动态委派出色地解决了委派、bean 及其业务接口之间的耦合问题。但是,它并不是完美的解决方案,也不会总是最好的解决方案。虽然您从这种方法中获得了极大的灵活性,但也付出了性能代价。Java 反射并不十分快,因此在调用 invoke() 和获得结果之间,您会感到一些延时。前一篇技巧文章中展示的静态业务委派类是更快的解决方案,但它使您的业务层和技术层的耦合程度比您所希望的要高。因此,在权衡这两个选择时,选择哪一个要根据设计或性能而定。当应用程序的设计比整体性能更重要时,动态委派是更好的选择。当性能是更重要的因素时,业务委派是更好的选择。

您可能会发现自己正在将动态委派用于内部网应用程序,在内部网中,所有机器都在本地网络上,并且您会经常添加或更改功能。对于电子商业和面向顾客的应用程序,原始的业务委派可能是更好的选择。在这两种情况下,您现在都应该对业务委派及其工作原理有了更好的理解。好好玩,玩得开心点,我们网上再见!

2.EJB best practices: The dynamic delegate(英文原版) [Re: palatum] Copy to clipboard
Posted by: palatum
Posted on: 2003-03-17 18:57

EJB best practices: The dynamic delegate

Build more generic business delegates with Java reflection
Level: Intermediate


Brett McLaughlin (brett@oreilly.com)
Author and Editor, O'Reilly and Associates
November 2002

While the business delegate class does bring exciting new flexibility to your enterprise Java designs, it can be tedious to code up a business delegate for every session bean in your application. In this installment of his EJB best practices series, Brett McLaughlin shows you how to create an even more generic version of the business delegate class: the dynamic delegate.
In the last tip, we discussed how to use a business delegate class (not to be confused with the Business Interface pattern) to access your EJB components. By inserting a business delegate class between our client code and the EJB code, we were able to insulate our application's Web tier from both EJB semantics and business logic.

One way to look at this type of design is in terms of how generic it is. Starting with an application in which business logic and technology functions are tightly entwined, we've been gradually separating out the different layers of the application and using different techniques to alleviate their interdependency. In doing so, you should be finding that the more generic an application's underlying structure is, the more robust and maintainable it is over time.

In this tip, we'll continue working with the idea of generic design. We'll start by exploring the limits of our current business delegate implementation, then I'll show you how to overcome those limitations by creating an even more generic, and thus less rigid, implementation of the business delegate class.

The business delegate class: A refresher
Recall the business delegate class for our Library bean interface we discussed last month.

Most of the LibraryDelegate class merely duplicates the methods in the original Library bean. The LibraryDelegate adds init(), destroy(), and constructor methods, and then uses these methods to delegate tasks to the Library bean. In doing so, the delegate acts as a buffer between the Web tier and your enterprise beans. Here's the original bean's business interface.

A proliferation of methods
The problem with this approach doesn't become evident until you consider that many session beans have 10, 20, or more methods. In fact, it isn't uncommon to find a session bean with 50 or more methods! Because a bean's business interface must contain all the methods of the bean, so will the business delegate class. That makes for bloated code, and plenty of room for error.

Are you too quick on the keys?
When working with EJB components, we're often duplicating a number of methods across remote interfaces, business interfaces, implementation classes, and now business delegates. Rather than type the methods in manually, many of us prefer to cut and paste them between editor windows and IDEs, but beware: it's just as easy to make a mistake hitting Option+V or Control+V as it is when typing methods in manually -- and the chance of error goes up as you add more methods. By double-checking that your methods are properly entered and that you've cut and pasted them as you intended to, you'll save yourself a lot of grief in the long run.
In addition to bloat, we also have to consider the change factor. Because the Delegate class has to duplicate all the bean's methods, and because beans do inevitably change over time, you could find yourself spending a lot of time adding new methods to the Delegate, not to mention recompiling and, possibly, testing the new code.

By itself, this may not seem like much of a problem. But consider that we started using a business delegate class to abstract the business and presentation logic from the technology infrastructure, in this case EJB components. If changing the remote interface requires a change to the business delegate, then our business delegate is, in effect, still tied to the underlying components.

There has to be a better way, you say. And there is.

The dynamic delegate
The solution is to use a dynamic delegate, which in turn uses Java reflection. Instead of having each business method hardcoded into your delegate, you can have the delegate dynamically invoke the methods (through the Java Reflection API) on the remote interface of the targeted EJB component. This allows a complete decoupling from the remote interface, in that adding a method to the business or remote interface of your beans requires no corresponding changes in the business delegate. It's also easier to make changes to your technology infrastructure with the dynamic delegate. Migrating from a remote interface and EJB technology to another technology such as Java Data Objects (JDO) would require you to change only the delegate's init() method. All the other method calls would continue to be delegated through the bean's interface, and to work with no further changes. Listing 1 shows a dynamic version of the Library business delegate:

Listing 1. A business delegate for a Library bean
package com.ibm.library;

import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;
import java.rmi.RemoteException;
import java.util.HashMap;
import java.util.Map;
import javax.ejb.CreateException;
import javax.naming.NamingException;

public class LibraryDelegate implements ILibrary {
private ILibrary library;
private Map availableMethods;

public LibraryDelegate() {
init();
}

public void init() {
// Look up and obtain our session bean
try {
LibraryHome libraryHome =
(LibraryHome)EJBHomeFactory.getInstance().lookup(
"java:comp/env/ejb/LibraryHome", LibraryHome.class);
library = libraryHome.create();

// Get the methods available for use in proxying
availableMethods = new HashMap();
Method[] methods = ILibrary.class.getMethods();
for (int i=0; i<methods.length; i++) {
availableMethods.put(methods[i].getName(),
methods[i]);
}
} catch (NamingException e) {
throw new RuntimeException(e);
} catch (CreateException e) {
throw new RuntimeException(e);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}

// All the hard-coded methods are removed
public Object
invoke(Object proxy, Method method, Object[] args)
throws Throwable{

try {
// See if this is init() or destroy()
if (method.getName().equals("init")) {
init();
return null;
} else if (method.getName().equals("destroy")) {
destroy();
return null;
} else {
Method method =
(Method)availableMethods.get(method.getName());

// See if we found anything
if (method != null) {
return method.invoke(library, args);
} else {
throw new
NoSuchMethodException("The Library does not " +
"support the " + method.getName() +" method.");
}
}
} catch (InvocationTargetException e) {
// We don't support throwing RuntimeExceptions from EJBs
// directly
if (e.getTargetException() instanceof RemoteException) {
throw new RuntimeException(e);
} else {
throw e.getTargetException();
}
}
}

public void destroy() {
// In this case, do nothing
}
}



The dynamic delegate nicely solves the problem of coupling between the delegate, the bean, and its business interface. It isn't a perfect solution, however, nor is it always the best one. While you gain a tremendous amount of flexibility with this approach, you also pay a performance penalty. Java reflection isn't terribly fast, so you'll experience some lag time between calling invoke() and getting your results. The static business delegate class shown in the previous tip is a faster solution, but keeps your business and technology layers more coupled than you would like them to be. So, when weighing the two options, the choice comes down to design or performance. The dynamic delegate is the better choice when your application's design is more important than its overall performance. The business delegate is a better bet when performance is the more important factor.

You might find yourself using the dynamic delegate for intranet applications, where all the machines are on a local network and you're constantly adding or changing functionality. For e-commerce and customer-facing applications, the original business delegate is likely the better choice. In either case, you should now have a better understanding of business delegates and how they work. Play around, have fun, and I'll see you online!

3.Re:EJB 最佳实践:动态委派 [Re: palatum] Copy to clipboard
Posted by: lummyliao
Posted on: 2003-03-21 16:45

Thank you very much !!!


   Powered by Jute Powerful Forum® Version Jute 1.5.6 Ent
Copyright © 2002-2021 Cjsdn Team. All Righits Reserved. 闽ICP备05005120号-1
客服电话 18559299278    客服信箱 714923@qq.com    客服QQ 714923