上篇:使用注释开发pojo服务
对开发服务器端应用程序而言,java企业版本即java ee(以前叫j2ee)是一个功能强大、但又过于复杂的平台。很早以来,过于复杂历来被认为是阻碍人们采用java ee的一个重要因素。
但在过去的三年,java开放源代码社区、java社区组织(jcp)以及主要的java ee开发商都在致力于简化java ee。譬如说,实际的应用程序使用新的设计范例来简化java ee的开发,譬如普通java对象(pojo)服务、服务拦截器和依赖注入。而诸多新的工具和框架也得到了广泛采用,用于同样的目的,譬如hibernate、面向方面编程(aop)、struts、xdoclet和spring。
这些模式和工具让刚入门的开发人员更容易上手,同时提高了经验丰富的java开发人员的生产力,目前它们正在被jcp集成到下一代java ee标准(即ejb 3.0)当中。java开发人员raghu kodali最近开展的一项调查表明,把sun的java ee示例应用程序rosterapp从ejb 2.1移植到ejb 3.0可以减少50%以上的代码。
java注释是ejb3.0的重要特性,它把pojo服务、pojo持久性和依赖注入联系起来,成为完整的企业中间件解决方案。本文使用了一个示例应用程序:jboss ejb 3.0 trailblazer,以演示开发添加注释的轻便型ejb 3.0 pojo应用程序。trailblazer应用程序多次使用ejb 3.0中的不同工具和api,实现了一个投资计算器。示例应用程序在jboss 应用服务器4.0.3里面以非传统方式运行,完全符合最新的ejb 3.0规范(公众预览版)。
ejb 3.0的注释驱动编程模型
从开发人员的角度来看,ejb 3.0广泛使用java注释。注释有两个重要优点:它们取代了过多的xml配置文件,而且不需要严格的组件模型。
注释与xml
基于xml的部署描述符和注释都可以用来配置java ee应用程序中的服务相关属性。两者的区别在于:xml文件与代码分开处理(往往在运行时);而注释与代码一起编译,而且由编译器进行检查。这对开发人员产生了以下这些重要影响:
● 冗长性:xml配置文件以冗长出名。为了配置代码,xml文件必须从代码地方复制许多信息,譬如类名称和方法名称。另一方面,java注释却是代码的一部分,不需要另外引用代码,就可以指定配置信息。
● 健壮性:xml配置文件中的复制代码信息带来了多个潜在故障点。譬如说,如果拼错了xml文件中的方法名称,应用程序会在运行时出错。换句话说,xml配置文件不如注释来得健壮。注释可以由编译器来检查,同代码的其余部分一起处理。
● 灵活性:因为xml文件与代码分开处理,所以基于xml的配置信息不是“硬编码”的,以后可以改动。部署时间的灵活性对系统管理员来说是一项很好的特性。
注释使用简单,足以满足大多数应用程序的要求。xml文件比较复杂,可用来处理更高级的问题。ejb 3.0允许通过注释来配置大多数应用程序的设置。ejb 3.0还支持xml文件用于取消默认的注释值、配置外部资源(如数据库连接)。
pojo与严格组件
除了取代及简化xml描述符外,注释还可以让我们弃用曾困扰ejb 1.x和ejb 2.x的严格的组件模型。
ejb 组件是容器管理的对象。容器在运行时操纵bean实例的行为和内部状态。为了让这种行为出现,ejb 2.1规范定义了bean必须遵守的严格的组件模型。每个ejb类必须从为容器提供回调钩子(callback hook)的某个抽象类继承而来。因为java只支持单一继承,严格的组件模型就限制了开发人员使用ejb组件创建复杂对象结构的能力。读者会在本文下篇分看到,如果映射实体bean中复杂的应用程序数据,这更是个问题。
在ejb 3.0中,所有容器服务都可以通过注释进行配置,并提供给应用程序里面的任何pojo。大多数情况下,不需要特殊的组件类。
开发松散耦合的服务对象
java ee等企业中间件的最重要的好处之一就是,让开发人员可以使用松散耦合的组件来开发应用程序。这些组件仅仅通过已发布的业务接口来进行耦合。因此,可在不改变应用程序其余部分的情况下,改变组件实现类。这样使应用程序更健壮、更容易测试,而且更容易移植。ejb 3.0简化了在pojo中构建松散耦合的业务组件。
会话bean
在ejb 3.0应用程序中,松散耦合的服务组件通常作为会话bean来实现。会话bean要有一个接口(即业务接口),那样其他应用程序的组件就可以通过它使用其服务。下面的代码为我们的示例投资计算器服务提供了业务接口。根据投资者开始投资时及终止投资时的年龄、基金增长率及每月储蓄额,它只有一个方法来计算总的投资回报。
public interface calculator {
public double calculate (int start, int end, double growthrate, double saving); }
会话bean类仅仅实现了业务接口。必须通过为其添加无状态或者有状态的注释,告诉ejb 3.0容器这个pojo类是会话bean。有状态的会话bean可以在几个不同的服务请求期间保持客户端状态。与之相反,无状态的会话bean的请求每次都是由随机的会话bean实例来处理。其行为与原来ejb 2.1中的有状态和无状态的会话bean的行为相一致。ejb 3.0容器计算出什么时候为bean对象创建实例,然后通过业务接口来提供。下面是会话bean实现类的代码:
|
还可以为一个会话bean指定多个接口-一个用于本地客户端,一个用于远程客户端。只要使用@local和@remote注释,就可以区别接口。下面的代码片断显示了calculatorbean会话bean同时实现了本地接口和远程接口。如果你没有@local和@remote注释,会话bean接口就是默认的本地接口。
|
会话bean用户通过java命令和目录接口(jndi)得到bean的存根对象。由容器提供的存根对象实现了会话bean的业务接口。针对存根对象的所有调用都被转向容器,并针对可管理的bean实例进行调用。至于无状态的会话bean,每次进行调用时,都能获得新的存根对象。至于有状态的会话bean,必须把存根对象缓存在客户端上,那样容器就知道以后每次调用时为你提供相同的的bean实例。下面的代码片断显示如何调用会话bean。这里介绍获得bean存根对象的一种更简单的方法。
|
会话bean的生命周期管理
为了实现松散耦合,应用程序把会话bean实例的创建、缓存、销毁全部交给ejb 3.0容器(即反向控制设计模式)。而应用程序只处理业务接口。
但如果应用程序需要对会话对象实行粒度更细的控制,该如何呢?譬如说,应用程序可能需要在容器创建会话bean时执行数据库初始化,或者在销毁bean时需要关闭外部连接。只要在bean类中实现生命周期回调方法,就能实现这些操作。这些方法由容器在bean生命周期的不同阶段(如bean创建和销毁)进行调用。在ejb 3.0中,可以指定任何bean方法作为回调,只要为其添加下列注释。不像ejb 2.1里面,所有的回调方法都必须加以实现,即便回调方法是空的;ejb 3.0 bean可以有好多回调方法,可以是任何方法名称。
● @postconstruct:bean实例创建后,容器立即调用添加了注释的方法。这个注释同时适用于有状态和无状态的会话bean。
● @predestroy:容器从对象池当中销毁闲置或者过期的bean实例之前,调用添加了注释的方法。这个注释同时适用于有状态和无状态的会话bean。
● @prepassivate:如果某个有状态的会话bean实例闲置时间过长,容器就会将它挂起(passivate),并把其状态保存在缓存当中。容器将bean实例挂起之前,调用由这个注释作以标记的方法。这个注释适用于有状态的会话bean。
● @postactivate:如果客户端再次使用已被挂起的的有状态的会话bean时,新的实例被创建,bean状态被恢复。如果被激活的bean实例准备就绪,就调用由该注释作以标记的方法。这个注释只适用于有状态的会话bean。
● @init:这个注释为有状态的会话bean指定了初始化方法。它有别于@postconstruct注释之处在于:在有状态的会话bean中,可以用@init对多个方法作以标记。不过,每个bean实例只能有一个@init方法被调用。ejb 3.0容器决定调用哪个@init方法,具体取决于bean是如何创建的。@postconstruct方法在@init方法之后被调用。
生命周期方法的另一个有用注释是@remove,对有状态的会话bean来说更是如此。应用程序通过存根对象调用使用@remove标注的方法时,容器就知道在该方法执行完毕后,把bean实例从对象池当中移走。下面是这些生命周期方法注释在calculatorbean中的一个示例:
|
消息驱动的bean
会话bean服务通过同步方法调用来提供。另一种重要的松散耦合的服务就是,由入站消息触发的异步服务,入站消息包括电子邮件或者java消息服务(jms)消息。ejb 3.0消息驱动的bean(mdb)是为了处理基于消息的服务请求而设计的组件。
mdb类必须实现消息监听器(messagelistener)接口。当容器检测到该bean的消息后,就调用onmessage()方法,并把入站消息作为调用参数传递。mdb会决定在onmessage()方法中如何处理消息。可以用注释来配置这个mdb监控哪些消息队列。mdb部署后,容器使用注释里面指定的配置信息。在下面的示例中,当容器检测到queue/mdb jms队列中的入站消息后,就会调用calculatorbean mdb。mdb会解析消息,并根据消息内容执行投资计算。
|
依赖注入
在前面一节中,介绍了如何开发松散耦合的服务组件。然而,为了使用这些服务对象,你需要通过服务器的jndi来查询存根对象(用于会话bean)或者消息队列(用于mdb)。jndi查询是把客户端从实际实现的服务对象解除耦合的一个关键步骤。不过,基于字符串名的普通jndi查询并不方便。以下是几个原因:
● 客户端与服务端必须就基于字符串的名字达成一致。这不是由编译器或者任何部署时间检查所执行的契约。
● 已获取的服务对象在编译时不进行检查,可能会导致运行时出现数据类型转换错误(casting error)。
● 应用程序里面一再出现冗长的查询代码,该代码有自己的try-catch代码块。
ejb 3.0采用了一种简单、便利的方法,把解除耦合的服务对象和资源提供给任何pojo使用。你使用@ejb注释,就可以把ejb存根对象注入到ejb 3.0容器管理的任何pojo中。如果对某字段变量标以注释,容器会在第一次访问之前,为该变量赋予正确的值。下面的示例显示了如何把calculatorbean无状态会话bean的存根对象注入到calculatormdb mdb类中。
|
如果对某个属性的javabean风格的设置方法标以注释,属性第一次使用之前,容器会自动用正确的参数调用属性设置方法。下面的代码片断演示了工作过程:
|
除@ejb注释外,ejb 3.0还支持@resource注释注入来自jndi的任何资源。下面的例子演示了如何注入服务器的默认的timerservice和sessioncontext对象,并且演示了如何注入来自jndi的命名数据库和jms资源。
|
此外,你还可以把容器管理的持久性管理器(即实体管理器――类似hibernate会话对象)注入到ejb 3.0 pojo中。
为pojo提供容器服务
除了管理松散耦合的服务对象的生命周期和访问外,ejb 3.0容器还通过简单的注释为可管理的pojo提供运行时服务。
事务
最有用的容器服务可能就是事务服务:万一应用程序出现错误或者异常,它可以保证数据库的完整性。你只要为pojo方法添加注释,即可声明事务属性。容器可以在适当的事务上下文中运行方法。譬如说,下面的代码声明:容器应当创建新的事务来运行updateexchangerate()方法。如果该方法存在,事务就提交。实际上,从updateexchangerate()里面被调用的所有方法也都在同样的事务上下文中执行,除非以其他方式进行显示声明。updateexchangerate()方法中执行的数据库操作要么全部成功,要么全部失败。
|
安全
容器还能提供验证用户身份的安全服务,并且可以根据用户角色,限制对可管理的pojo的访问。对每个pojo类而言,你可以使用@securitydomain注释指定安全域,它能告诉容器到哪里去找密码和用户角色列表。jboss里面的other域表明文件是类路径中的users.propertes和roles.properties文件。然后,对于每个方法,你可以使用安全限制注释来指定谁可以运行这个方法。譬如在下面例子中,容器对所有试图执行addfund()方法的用户进行验证,只允许角色是adminuser的用户才能实际运行。如果你没有登录,或者不是以管理员的身份登录,就会引发安全异常。
|
通用拦截器
事务服务和安全服务都可以被看成是由容器管理的运行时拦截器。容器拦截来自ejb存根对象的方法调用后,为调用添加事务上下文或者安全限制。
在ejb 3.0中,你可以自己编写拦截器来扩展容器服务。使用@aroundinvoke注释,就可以把任何bean方法指定为在其他任何bean方法运行前后执行的拦截器方法。在下面例子中,log()方法是分析及记录其他bean方法的执行时间的拦截器:
|
链接:用ejb 3.0简化企业java开发(下)
闽公网安备 35060202000074号