EJB 3.0+Aspect实现声明性编程初步

编程

  提要 本文将与你一同探讨怎样把注解和方面的威力联合起来,以与EJB 3.0兼容的方式为企业实现提供声明性服务,而在同时仍然提供容器的独立性。

  一、 引言

  在我们共同寻求进一步提高软件开发生产性能的方法的过程中,我们-作为Java社团成员-一般都转向J2EE来提供针对企业开发中更具挑战性的技术问题如分布式事务管理、并发性和对象分布等的解决方案。其背后的指导思想-这些复杂的企业服务能被应用程序服务器供应商所实现并能为商业开发者所平衡-的确是一种很好的思想。J2EE,具体地说是EJB,已成功地提供了一个平台-在其上构建企业Java应用程序。

  这其中部分的成功是由于能够进行声明性编程-一种程序开发方式-用这种方式,你可以声明基础结构服务而不是用商业逻辑明确地编码从而使代码散布于各处。EJB已经证明了这种编程方式的价值-通过允许企业问题例如事务和安全被用一种发布描述符所声明并为容器所处理。

  然而,在过去的岁月中,越来越多的开发者认识到EJB在团队的生产效率方面给它自己带来新的大量的挑战-每个EJB必须伴随多个接口,以一种发布描述符描述,经由JNDI被存取,等等。而在容器外EJB上进行单元测试也带来另外的困难,如今EJB已不再把重点放在单纯的面向对象开发上。

  请注意,为阅读本文您需具备如下工具:

  ·Java 2 SDK 1.5

  ·Maven 2.0 Beta 2

  EJB 3.0的目标在于从以下几个方面使企业开发更为容易:

  ·通过引入元数据注解来实现声明性请求企业服务

  ·经由注解实现依赖性/资源注入

  ·实现企业beans与EJB特定接口的解耦

  ·经由轻量级的对象关系映射实现持续性存储的简化

  这对于EJB开发者来说尤如一股春风-一直以来,他们竭力地从事开发、测试和维护EJB。利用EJB 3.0写一个企业bean现在变得很容易,就如用特定的注解创建一个POJO(传统的Java对象)以把它标明为一个EJB并请求企业服务。下面是一个来自于EJB 3.0 Public Draft中EJB的例子:

@Stateful
public class CartBean implements ShoppingCart
{
private float total;
private Vector productCodes;
public int someShoppingMethod(){...};
...
}

  EJB 3.0声明中实质上指明开发者需要的不是一重量级的、"一次发布满足所有"的解决方案,而是一个轻量级的、容易使用的解决方案-为开发者提供一定范围的企业服务。为此,EJB 3.0所提供的最重要的方法之一就是实现企业beans与EJB API的解耦。并且,此解决方案还带来令人感兴趣的衍生-EJB现在不仅能够运行在不同的EJB容器上,而且还能运行于任何应用程序框架内部-这些框架必须能够识别EJB 3.0(JSR 220)和用于声明企业服务的普通注解(JSR 250)。

  本文没有提供关于声明性编程、EJBs、方面或注解的深度探索。相反,而只是分析一下这些技术之间的相互关系并讨论如何把它们用一种新的方式结合起来以简化应用程序开发。

  在本文中,你将会学习到如何编写一个EJB 3.0兼容的bean并且通过创建几个简单的方面使其具有声明性事务管理、安全和资源注入等功能。我希望您能从这个练习中得到以下的受益:

  ·学习方面的三个实际应用(依赖性注入、安全和事务)。

  ·熟悉EJB 3.0及其背后的思想。

  ·认识到怎样实现EJB与特定API的解耦以允许EJB 3.0兼容的服务能够以轻量级实现而不是仅由EJB来提供。

  二、 实例应用程序-航班订购

  在整个后面的讨论中,你将学习到一个航班订购系统的实现-它使用方面和注解来实现依赖性注入、安全和事务管理。该应用程序仅执行两项功能:它允许用户搜索航班(图1),然后订购一次旅行(图2)。这两个操作都将被进行安全处理以仅允许能被识别的用户来执行它们。另外,既然"订购旅行"操作包含订购两个航班(外出和返回航班),那么需要把该操作创建为事务性的-如,两个订购将作为一个工作单元要么都成功要么都失败。


图1.航班查询:首先,用户查找满足他们的指定标准的航班。

图2.航班订购:接下来,用户订购一个外出航班和一个返回航班。两个订购要么都成功要么都失败。
  这个简单的Web应用程序包含几个servlet、一个服务外观和一个DAO层(见图3)。

  资源配置、安全性和事务管理等横切关注点将由方面(用AspectJ 1.5 M3实现)所提供以实现在Java 5注解中所声明的注入行为。


图3.航班订购系统架构:这个航班订购系统包括三个主要组成组件-它们联合起来共同完成用户请求。  三、 资源注入

  EJB 3.0草案声明中允许资源经由@Resource注解来声明(这一决定定义在草案普通注解声明中)并且被容器注入进你的EJB。依赖性注入是一项技术-使用这种技术,一个对象外部的实体而不是显式地为该对象所创建的实体能够提供(注入)一个对象的依赖性。它有时被描述为好莱坞原则-这开玩笑似地意味着"不要给我们打电话,我们会给你打电话的"。

  以TravelAgencyServiceImpl类为例-这个类为了持续性存储一些数据需要找到一个IFlightDAO接口的实现。传统地,这是经由一个工厂、singleton、服务定位器或一些另外的定制解决方案来实现的。其中,一个可能的解决方案看上去如下所示:

public class TravelAgencyServiceImpl implements ITravelAgencyService
{
 public IFlightDAO flightDAO;
 public TravelAgencyServiceImpl()
 { flightDAO = FlightDAOFactory.getInstance().getFlightDAO(); }
 public void bookTrip(long outboundFlightID, long returnFlightID, int seats)
 throws InsufficientSeatsException
 {
  reserveSeats(outboundFlightID, seats);
  reserveSeats(returnFlightID, seats);
 }
}

  你已看到,这个实现包含创建一个特定的工厂类-它很可能读取存储在某处的配置信息以了解要创建IFlightDAO的实现方式。如果不是让服务显式地创建它的由容器所注入的依赖性,那么配置细节和对象创建将被代理到容器上。这允许一个应用程序中的组件能够被容易地连接到一起-用不同的配置并且消除大量老式的singleton和工厂代码。

  该类的一个实现-它依赖于一个用JSR 250资源注解所声明的IFlightDAO的实现-可能看上去如下所示:

public class TravelAgencyServiceImpl implements ITravelAgencyService
{
 @Resource(name = "flightDAO")
 public IFlightDAO flightDAO;
 public void bookTrip(long outboundFlightID, long returnFlightID, int seats)
 throws InsufficientSeatsException
 {
  reserveSeats(outboundFlightID, seats);
  reserveSeats(returnFlightID, seats);
 }
}

  在这种情况下,容器将把一个命名为"flightDAO"的资源的正确实现提供给服务类。但是,如果你现在就想利用资源注入,而不是等待EJB 3.0发行版,又该如何呢?好,你可以采用一种轻量级的容器-它能够提供例如Spring或Pico Container的依赖性注入。然而,当前我还不了解存在一个轻量级的容器-它能够使用JSR 250资源注解以指定注入要求(尽管我非常盼望在这一方面出现一些)。

  一种解决方案是使用方面来实现依赖性注入。如果你为此使用@Resource注解,那么你的实现将与EJB 3.0方式一致并且向前兼容EJB 3.0实现-而实现这并不是很困难的事情。下列列表显示用AspectJ创建的一个方面-它注入用@Resource注解所注解的字段:

@Aspect
public class InjectionAspect
{
 private DependencyManager manager = new DependencyManager();
 @Before("get(@Resource * *.*)")
 public void beforeFieldAccesses(JoinPoint thisJoinPoint)
 throws IllegalArgumentException, IllegalAccessException
 {
  FieldSignature signature = (FieldSignature) thisJoinPoint.getSignature();
  Resource injectAnnotation = signature.getField().getAnnotation(Resource.class);
  Object dependency = manager.resolveDependency(signature.getFieldType(),injectAnnotation.name());
  signature.getField().set(thisJoinPoint.getThis(), dependency);
 }
}

  这个简单方面所做的全部是,从一个属性文件(这个逻辑被封装在DependencyManager对象中)查询实现类并且在存取字段之前把它注入到用@Resource注解所注解的字段中。显然,这种实现不是完整的,但是它确实说明了你可以怎样以一种JSR 250兼容方式且不需采用EJB来提供资源注入。

  四、 安全性

  除了资源注入外,JSR 250和EJB 3.0还提供经由注解的元数据安全表示。javax.annotation.security包定义了五个注解-RunAs,RolesAllowed,PermitAll,DenyAll和RolesReferenced-所有这些都能应用到方法上来定义安全要求。例如,如果你想要声明上面列出的bookFlight方法仅能为具有"user"角色的调用者所执行,那么你可以用如下的安全约束来注解这个方法:

public class TravelAgencyServiceImpl implements ITravelAgencyService
{
 @Resource(name = "flightDAO")
 public IFlightDAO flightDAO;
 @RolesAllowed("user")
 public void bookTrip(long outboundFlightID, long returnFlightID, int seats)
 throws InsufficientSeatsException
 {
  reserveSeats(outboundFlightID, seats);
  reserveSeats(returnFlightID, seats);
 }
}

  这个注解将指出由容器来负责保证只有指定角色的调用者才能执行这个方法。因此现在我将展示另一个简单的方面-它将进一步加强在该应用程序上的安全约束:

@Aspect
public class SecurityAspect
{
 @Around("execution(@javax.annotation.security.RolesAllowed * *.*(..))")
 public Object aroundSecuredMethods(ProceedingJoinPoint thisJoinPoint)
 throws Throwable
 {
  boolean callerAuthorized = false;
  RolesAllowed rolesAllowed = rolesAllowedForJoinPoint(thisJoinPoint);
  for (String role : rolesAllowed.value())
  {
   if (callerInRole(role))
   { callerAuthorized = true; }
  }
  if (callerAuthorized)
  { return thisJoinPoint.proceed(); }
  else
  {
   throw new RuntimeException("Caller not authorized to perform specified function");
  }
 }
 private RolesAllowed rolesAllowedForJoinPoint(ProceedingJoinPoint thisJoinPoint)
 {
  MethodSignature methodSignature = (MethodSignature) thisJoinPoint.getSignature();
  Method targetMethod = methodSignature.getMethod();
  return targetMethod.getAnnotation(RolesAllowed.class);
 }
 private boolean callerInRole(String role)
 { ... }
}

  这个方面包含了所有方法的执行-通过核实该调用者是在注解中所指定的角色之一,用@RolesAllowed注解来注解并且保证调用者被授权调用该方法。当然你还能代之以任何你喜欢的算法来授权用户并且检索他/她的角色,例如JAAS或一个定制的解决方案。在本示例程序中,为方便起见,我选择代理到servlet容器。

  五、 事务

  事务成为企业开发的一个重要部分-因为它们有助于在一个并发的环境中的数据集成。从一个高层次上看,事务可以通过多种或者是完整的或者是都不完整的操作来保证这一点。

  不象针对资源注入和安全的注解,针对事务的注解是特定于EJB 3.0的并且没有在JSR 250普通注解中定义。EJB 3.0定义了两个与事务相联系的注解:TransactionManagement和TransactionAttribute。该TransactionManager注解指定事务是由容器所管理还是为bean所管理的。在EJB 3中,如果这个注解没被指定,那么将使用容器所管理的事务。TransactionAttribute注解用于指定方法的事务传播级别。有效值-包括强制的、要求的、要求新的、支持的、不支持的和从不支持的-用来定义是否要求一个已有事务或启动一个新的事务,等等。

  因为bookFlight操作包含两步-订购一个外出航班和一个返回航班,所以,通过把它包装成一个事务,你能保证这项操作的一致性。通过使用EJB 3.0事务注解,这将看上去如下所示:

public class TravelAgencyServiceImpl implements ITravelAgencyService
{
 @Resource(name = "flightDAO")
 public IFlightDAO flightDAO;
 @RolesAllowed("user")
 @TransactionAttribute(TransactionAttributeType.REQUIRED)
 public void bookTrip(long outboundFlightID, long returnFlightID, int seats)
 throws InsufficientSeatsException
 {
  reserveSeats(outboundFlightID, seats);
  reserveSeats(returnFlightID, seats);
 }
}

  并且你可以应用一个简单的方面来自动地界定事务边界:

@Aspect
public class TransactionAspect
{
 @Pointcut("execution(@javax.ejb.TransactionAttribute * *.*(..))")
 public void transactionalMethods(){}
 @Before("transactionalMethods()")
 public void beforeTransactionalMethods()
 { HibernateUtil.beginTransaction(); }
 @AfterReturning("transactionalMethods()")
 public void afterReturningTransactionalMethods()
 { HibernateUtil.commitTransaction(); }
 @AfterThrowing("transactionalMethods()")
 public void afterThrowingTransactionalMethods()
 { HibernateUtil.rollbackTransaction(); }
}

  这个实现基于这样的假设-Hibernate和无所不在的线程本地模式被用于管理Hibernate会话和事务对象;但是,任何其它适当的实现,例如基于JTA的实现,都能被代替使用。

  六、 小结

  通过使用EJB 3.0和JSR 250注解集,本文已经展示了例如资源管理、安全和事务等横切关注点是怎样被实现为方面的。当然,还有许多内容我们需进一步学习。首先要学的就是通过使用AspectJ的实现这些示例方面为模块化横切关注点所提供的蓝图。其次,我们已经看到了在如今正浮出水面的EJB 3.0声明背后的一些新思想和新概念。最后,我们还以戏剧性的方式看到了从EJB API中解耦我们的商业对象所必须要提供的自由。在这一点上,所有你想使TravelAgencyServiceImpl成为一个无状态会话要做的仅是添加一条最后的注解:

@Stateful
public class TravelAgencyServiceImpl implements ITravelAgencyService
{ ... }

  最后,我非常希望这种自由地提供企业服务的方式会带来框架/容器工业界的竞争和革新。

时间: 2024-09-25 06:38:58

EJB 3.0+Aspect实现声明性编程初步的相关文章

用Python创建声明性迷你语言的教程_python

大多数程序员考虑编程时,他们都要设想用于编写应用程序的 命令式样式和技术.最受欢迎的通用编程语言(包括 Python 和其它面向对象的语言)在样式上绝大多数都是命令式的.另一方面,也有许多编程语言是 声明性样式,包括函数语言和逻辑语言,还包括通用语言和专用语言. 让我们列出几个属于各个种类的语言.许多读者已经使用过这些工具中的许多工具,但不见得考虑过它们之间的种类差别.Python.C.C++.Java.Perl.Ruby.Smalltalk.Fortran.Basic 和 xBase 都是简单

JavaBean(EJB) 3.0 全新体验_JSP编程

引言 期待以久的EJB3.0规范在最近发布了它的初稿.在本文中将对新的规范进行一个概要性的介绍,包括新增的元数据支持,EJBQL的修改,实体Bean模型访问bean上下文的新方法和运行时环境等等.作者还讨论了EJB在未来要作出的调整以及EJB3.0与其他开发规范之间的关系. 开始 无论如何由于EJB的复杂性使之在J2EE架构中的表现一直不是很好.EJB大概是J2EE架构中唯一一个没有兑现其能够简单开发并提高生产力的组建.EJB3.0规范正尝试在这方面作出努力以减轻其开发的复杂性.EJB3.0减轻

用EJB 3.0开发企业级Bean组件初体验

阅读提要 EJB 3.0规范的最终稿已经提交到JCP,如果不出意外EJB3.0将在2006年正式发布.本文作为探讨EJB 3.0公共草案三系列中的第一篇,将解释EJB 3.0和企业Bean组件的基本概念:另外,你还学习到怎样使用JBoss和Maven来开发基于EJB 3.0的企业级bean组件. 尽管EJB 3.0规范还没有正式发行,但是它已经在Java开发社群中引起广泛兴趣--无论是对其拥护者还是其竞争对手.所有人都承认迫切需要找到更有生产效率的软件开发方法:如今,他们的争论集中于在EJB 3

EJB 3.0规范全新体验

规范 引言 期待以久的EJB3.0规范在最近发布了它的初稿.在本文中将对新的规范进行一个概要性的介绍,包括新增的元数据支持,EJBQL的修改,实体Bean模型访问bean上下文的新方法和运行时环境等等.作者还讨论了EJB在未来要作出的调整以及EJB3.0与其他开发规范之间的关系. 开始 无论如何由于EJB的复杂性使之在J2EE架构中的表现一直不是很好.EJB大概是J2EE架构中唯一一个没有兑现其能够简单开发并提高生产力的组建.EJB3.0规范正尝试在这方面作出努力以减轻其开发的复杂性.EJB3.

POJO应用架构:Spring与EJB 3.0的对比

架构 爱因斯坦曾经说过:"每件事物都应该尽可能简单,而不是更简单".的确,对科学真理的追求都是为了简化理论的根本假设,这样我们才能处理真正麻烦的问题.企业级软件的开发也是这样的. 简化企业级软件开发的关键是提供一个隐藏了复杂性(例如事务.安全性和永续性)的应用框架.良好设计的框架组件可以提升代码的重复使用(reuse)能力,提高开发效率,从而得到更好的软件质量.但是,目前J2EE 1.4中的EJB 2.1框架组件被人们普遍认为是设计较差的和过于复杂的.Java开发者对EJB 2.1很不

EJB 3.0+Beehive开发客户反馈系统

设计目标 客户反馈系统作为公司与客户交流的平台,几乎为所有的企业所运用,最近,公司让我负责客户反馈系统的开发.由于,公司与国外客户的业务需要,该系统必须实现中,英,日三国语言的切换(国际化要求).在接到任务之后,我便决定尝试使用目前开源社区比较流行的Apache Beehive(蜂巢)和下一代EJB,EJB3.0等技术来实现这个系统. 开发环境 选择平台,开发工具 为了支持EJB3.0和Beehive,我们选择JBoss4.0.3应用服务器作为运行平台,它也是目前唯一提供EJB3.0容器的应用服

使用Apache OpenJPA开发EJB 3.0应用,第1部分: OpenJPA与EJB 3.0

简介: Java Persistence API(JPA)是 EJB 3.0 新引入的数据持久化编程模型,它 利用 Java 5 中的注释(Annotation)和对象/关系映射,为数据持久化提供了更 简单.易用的编程方式. 本系列 文章将全面介绍其开源实现 - Apache OpenJPA,将为学习 JPA 标准和使用 OpenJPA 进行实际的应用开发提供详细的指 南. 本文是系列文章的第一部分,概述了关系型数据库和面向对象之间的阻抗失谐 (impedance mismatch),介绍了 E

将遗留Hibernate应用程序迁移到OpenJPA和EJB 3.0(一)

简介:通过使用 EJB 2.1 以及 OpenJPA 和 EJB 3.0 中的等效功能比较 Hibernate 应用程序中的特 性和功能,学习如何将 Hibernate 应用程序源代码.对象关系映射和配置参数迁移到 OpenJPA. 引言 Hibernate 是开放源代码持久性和查询框架,提供传统 Java 对象 (POJO) 到关 系数据库表的与对象相关的映射,以及数据查询和检索功能.Apache OpenJPA 项目将按照 EJB 3.0 Java Persistence API 规范的定义

声明性 WCF安全性

目录 安全方案 Intranet 应用程序 Internet 应用程序 企业对企业应用程序 匿名应用程序 无安全性 声明性安全框架 实现 SecurityBehaviorAttribute 客户端声明性安全 SecurityHelper 和 SecureClientBase<T> 这一切意味着什么 安全性到目前为止是 Windows Communication Foundation (WCF) 最复 杂的领域.在服务端的每次 WCF 操作调用中,安全都由服务契约.操作契约. 错误契约(如果存在