【IT168 技术文章】Enterprise JavaBeans 技术一般分成三种核心类型的 bean:会话 bean、消息驱动 bean 和实体 bean。bean 还可以分成充当业务对象的 bean 和充当数据对象的 bean。会话 bean 和消息驱动 bean 是业务对象;实体 bean 是数据对象。大多数情况下,只需要将业务对象暴露给 Web 层(有时称为应用层),因为业务对象可以使用数据对象来处理数据存储。但在有些情况下,有必要允许用户直接访问和操作数据对象,这意味着要将实体 bean 暴露给 Web 层。这会使您的应用程序面临安全性威胁,而且随着您的应用程序的发展,还会导致杂乱无章的代码。
暴露的风险
实体 bean 揭示了大量有关数据库底层结构的信息。因为每个 bean 都包含用于数据库中的各个字段的取值(accessor)和赋值(mutator)方法(即, getxxx() 和 setxxx() ),而且因为方法名称通常与字段名称连在一起( User bean 中的 getFirstName() 通常访问 Users表中的 firstName字段),所以从应用程序的实体 bean 推断这个应用程序的整个数据库结构是可能的。尽管在单个应用程序中这可能不是什么大问题,但您的 bean 常常会暴露给其它网络上另外的应用程序。在 Web 服务系统中尤其会发生这种情况,其中应用程序都是跨多个网络链接的。
除了存在使您数据库结构暴露给未知应用程序的明显危险之外,您还应该考虑当数据结构更改时会发生什么。因为实体 bean 与您的数据结构紧密地结合在一起,所以随着数据库表中列的更改或新列的添加,实体 bean 常常要随之更改。通过将您的实体 bean 暴露给应用层,可以使它们的方法名称可用,而且这些方法名称几乎总是被您的应用程序和外部应用程序合并到新的应用程序代码中。随着您的实体 bean 的更改以及发展,就会出现严重的代码混乱。
会话虚包模式
幸运的是,有一个设计模式让您允许用户访问数据对象,同时又不会将实体 bean 暴露给 Web 层。 会话虚包(Session Facade)模式在实体 bean 和应用程序客户机之间放置了一个会话 bean。当我通过这种方法使用会话 bean 时,我喜欢把它当作 管理器,因为它的任务是管理对其它实体的访问。
会话 bean 旨在充当业务逻辑和应用逻辑之间的接口,逻辑极其简单,而且它只调出一个底层实体 bean。然而在实际情况中,您可能会选择让会话 bean 管理数据验证、安全性或任何其它特定于业务的功能。通过将实体 bean 封装在会话 bean 中,您可以访问所有所需的业务功能,同时不会“污染”您的实体 bean 代码。
为了解决工作示例中的问题,您可以创建称为 MovieManager 的会话 bean。 MovieManager bean 将包含旧方法 getDirector() 和新方法 getDirectors() 。当添加了新方法时,它只是被“代理”到您的实体 bean。因为第一个方法在实体 bean 中不再可用,所以会话 bean 使用助手方法来隐藏它,如清单 1 所示:
清单 1. 隐藏已不使用的方法
2 // We can't call getDirector() any more on the entity bean
3 // Call the new method, through this manager
4 List directors = getDirectors();
5 // Return the first one in the list
6 return (Person)directors.item(0);
7 }
8
清单 1 的适用性不是特别健壮,但它获得了成功。因为隐藏了旧方法(而不是删除),所以所有与它相关的应用程序代码都一直存在。通过将助手方法放置在它所属的业务对象中,您还将它排斥在实体 bean 之外。解决了这个紧急的问题后,您可以选择将 bean 的客户机手工移植到新方法,或让助手方法一直处理移植工作。不管是哪种方法,都不会影响您的应用程序代码和您的客户机代码。
隐藏数据结构
尽管上面的解决方案确实解决了更改管理的问题,但它不能消除安全性问题。您仍需要保护实体 bean(并由此保护您的数据结构),以免暴露给 Web 层。通过向会话 bean 添加一些简单的业务逻辑和数据操作功能,您不仅可以隐藏应用程序的数据结构,还可以提供对它所包含的信息的更复杂访问。
例如,随着应用程序的发展,您可能发现让用户依次访问各种数据对象(例如,导演、制片人、演员)的效率很低。因为这类信息几乎总是被放在一起,并一起使用,所以您可能发现重新确定会话 bean 的用途会很有帮助。您的会话 bean 将不包含 getDirector() 或 getDirectors() 方法,而是包含了如清单 2 所示的新的业务逻辑:
清单 2. 用会话 bean 掩盖数据结构
2 throws NamingException, RemoteException {
3 List crew = new LinkedList();
4 EJBHomeFactory f = EJBHomeFactory.getInstance();
5 MovieHome movieHome =
6 (MovieHome)f.lookup("java:comp/env/ejb/Movie", MovieHome.class);
7 Movie movie = movieHome.findByName(movieName);
8 crew.add(movie.getDirectors());
9 crew.add(movie.getProducers());
10 crew.add(movie.getExecutiveProducers());
11 // and so on...
12 return crew;
13 }
14 public List getCast(String movieName)
15 throws NamingException, RemoteException {
16 List cast = new LinkedList();
17 EJBHomeFactory f = EJBHomeFactory.getInstance();
18 MovieHome movieHome =
19 (MovieHome)f.lookup("java:comp/env/ejb/Movie", MovieHome.class);
20 Movie movie = movieHome.findByName(movieName);
21 crew.add(movie.getActors());
22 crew.add(movie.getStandIns());
23 // and so on...
24 return cast;
25 }
26
通过分离不同的应用程序层,以及使用业务逻辑来处理数据操作,您既阻止了对实体 bean 直接而且可能不安全的访问,又为您的 Web 层创建了更有意义的方法集。本例中,会话虚包充当了实体 bean 的封装器以及真正的业务逻辑单元,从而将原始数据转变成有意义的信息。
会话虚包模式是许多其它设计模式的基本构件,其优点远远不止这里所讨论的。在 EJB 非常好的实践的下一篇文章中,我们将使用业务接口(Business Interface)模式,以及您在这里了解到的某些诀窍来进一步抽象出使用您应用程序中所有类型 EJB 组件的过程。