2009年6月6日

总结(2)是针对EJB3的事务和安全中的容器管理事务(CMT),BMT及安全部分放在总结(3)中。
完全是自己学习的心得,还请前辈们指点。

 

EJB3的事务与安全

EJB3的是事务也符合ACID,即原子性、一致性、隔离型、持久性。这些特性与数据库事务一致,需要强调的是一致性,在事务开始前,系统是处于一种遵守业务规则和约束的一致状态下,那么在事务提交或回滚之后,系统也必须维持这种一致性状态。在事务进行过程中不必处于不一致性状态,事务在这里就像一个沙箱(sand-box)。

EJB中,事务也具有隔离级别的控制,但一般不用通过EJB容器来控制,而是在数据库资源这一级别来进行控制。

 

要知道在EJB容器中,代码层面的所有操作最终都是转化为两级的数据库操作,比如锁定和解锁数据库中的某行或某张表。事务日志来反映事务的变化,开始事务日志代表事务的开始,应用日志代表以提交该事务来结束,相反放弃日志就代表回滚事务而结束。

 

资源管理器(Resource Manager)的概念是管理特定某种资源的事务的组件。这个概念不仅包括关系数据库系统,也可以使消息服务器或其他业务系统。

 

如果只涉及一种单一资源的事务就称为local transaction本地事务,相反大多数企业应用都是需要涉及多种不同的资源。这时候就需要一种类型的组件来管理事务中多种资源,这个组件称为事务管理器(Transaction Manager),它在多个管理各自资源事务的资源管理之间进行协调和控制。

 

分布式的事务管理是通过一种称为两段式提交(two-phase commit)的机制来完成。现在最为流行的分布式事务协议是XA协议(XA Protocol),JavaEE就是通过该协议来完成分布式应用中的事务管理。

 

事务管理

JavaEE中事务使用JTAJava Transaction API,是建立在Java Transaction Service之上的服务),即javax.tranaction.UserTransaction接口,容器在后台会自动管理大多数事务细节,EJB开发者只需控制开始和停止事务、建立事务边界(transaction boundary)以及是否提交/回滚业务即可。

EJB3中提供了两种具体的事务方式,即容器管理的事务(Container-managed transactionCMT)和bean管理的事务(Bean-managed transactionBMT)。其中前者是使用声明式地或通过部署描述符来管理事务;后者需要以显式编码的方式来管理。但需要注意的是在EJB3中,只有SessionBeanMessageDrivenBean才支持CMTBMTJPA中并不直接依赖CMTBMT。但是当然,在任何JavaEE容器中都可透明得插入CMTBMT的事务环境。

EJB3中管理事务最简单灵活的方式是采用容器管理事务,即CMT

 

容器管理的事务,顾名思义,容器来做“事务开始、事务提交或回滚”。容器在调用方法前开始JTA事务,接着会调用业务方法,最后根据调用中发生的情况去决定是提交事务还是回滚事务。使用CMT事务管理,只需关注@TransactionManagement@TransactionAttribute注解,同时通过EJBContext的方法来回滚事务。

 

1@TransactionMangement注解用于向容器标识该bean的事务管理是使用CMT还是BMT,是通过该标签中的value属性来指明TransactionManagementType的枚举值。

 

2@TransactionAttribute注解,尽管由容器为我们来管理事务的各种细节,但是仍然需要通过该注解来告知容器如何去自动管理。

CMT对事务的管理可以在包装bean的方法时开始,也可以从调用者的事务中进行join开始。

TransactionAttributeType有几个枚举值:

(1)      REQUIRED:如果调用者没有事务,容器则创建新事务;如果调用者具有事务,容器则连接join调用者事务。

(2)      REQUIRED_NEW:如果调用者没有事务,容器就创建新事务;如果调用者有事务,容器暂停原来事务,并创建新的事务。

(3)      SUPPORTS:如果调用者没有事务,容器就不创建事务;反之则join并使用调用者的事务。

(4)      MANDATORY:如果调用者没有事务,容器抛出javax.ejb.EJBTransactionRequiredException异常;反之则连接并使用调用者的事务。

(5)      NOT_SUPPORTED:如果调用者没有事务,容器便不使用事务调用方法;反之则暂停调用者事务以non-transactional的方式调用方法。

(6)      NEVER:如果调用者没有事务,容器直接调用方法;反正抛出javax.ejb.EJBException异常。

 

a.    对于第1种情况,适合的场景是从non-transactionalWeb层调用bean方法,如果方法调用出现异常,那么容器不仅会rollback整个事务,同时也会向调用者抛出javax.transaction.RollbackException异常,以通知调用者事务已被回滚了。

b.    2种情况中,始终需要容器去创建新的事务,对于调用者原有的事物,容器就会暂停它,直到方法调用返回为止就恢复原有事务。这样的结果就是方法调用所创建的新事务无论成功与否,都不会对调用者原有的事务造成影响。

c.    SUPPORT选项表示本质上容器将会按照调用者的事务情况来处理,通常的场景是用于一些只读型的操作,比如检索数据记录等。

d.    MANDATORY表示对容器对事务的强制性要求,如果调用者存在事务,容器就join并使用调用者事务;但如果调用者没有事务环境,就会抛出EJBTransactionRequiredException异常。这种选项适合于如果方法调用失败进行回滚时也保证调用者环境的失败。

e.    对于NOT_SUPPORT,容器处理的本质是要求不能在事务环境下调用bean方法。如果调用者存在事务,容器会先暂停它,然后开始方法调用,在返回后恢复调用者的事务。

f.    NEVER表明在CMT中,容器不能允许从事务性环境中调用bean方法,否则会抛出EJBException异常。

 

对于前面这六种TransactionAttributeTypeMDB并不是全部都支持,它支持REQUIREDNOT_SUPPORT两种。这一点与MDB的特性有关:客户端无法直接调用MDB,所以事务属性中的SUPPORTREQUIRED_NEWMANDATORY就没有意义。

CMT事务管理的真正机制

很重要的一点:CMT方法要求容器回滚事务并不是立即进行的,而是向容器设置回滚标识,在事务结束的时候,容器检查该标志,如果不需回滚则提交该事务,如果需要回滚则进行。

通过EJBContext或其子类:SessionContextMessageDrivenContextsetRollbackOnly()方法,将回滚标志位置为true,当方法调用结束时,容器检查该状态以判断是否提交事务或是回滚事务。

需要注意的一点是,本质上,使用EJBContext进行设置标志位时,它是作为底层事务的一个上层抽象代理。所以使用时,必须保证有底层事务存在,也就是说必须在REQUIRED,REQUIRED_NEW,MADATORY这三种事务属性下使用!因为这三种事务属性都可以使容器保证底层事务的存在,即无论方法调用者是否存在事务环境,容器都会创建新事务。

setRollbackOnly()方法对应的是getRollbackOnly()方法,该方法返回当前EJBContext中回滚标志位的状态。该方法在业务处理中十分有用,考虑这样一个场景:

在进行一段非常长的资源密集型操作前如果前事务的前部分已经失败,那就是在一个注定要回滚的事务上付出很多代价。所以在这类操作之前应该先check一下rollbackOnly是否已经置为true

此外,处理该标志位状态的代码经常分布在业务逻辑的catch块中,如果catch了某种exception,则一边记录进log,一边通过set方法置状态位。在EJB3中,可以通过@ApplicationException注解使得“捕获异常转化为事务回滚”变成透明的机制。

@AplicationException注解

@javax.ejb.ApplicationException该注解可以用来控制事务性输出,将要抛出的异常类型在定义时加上该标签,同时设置该标签的rollback属性,即@ApplicationException(rollback=true/false)。应用异常(ApplicationException)是希望调用者或客户端处理的,一般认为除了java.rmi.RemoteExceptionjava.lang.RuntimeException之外都是应用异常。而从上述两种异常继承出的子类异常都被认为是系统异常,这种异常不会传递给调用者或客户端而是wrappedEJBException中。

添加此注解的异常无论是checked exception还是unchecked exception(比如运行时异常),都会被认为是应用异常(application exception),从而传递给方法调用者或客户端。

默认情况下,所有checked exception或所有被注解为应用异常的checked/unchecked exception,都不会以CMT的方式回滚,如果把rollback属性设置为true,则会通知容器失败时进行事务回滚,并且是在把应用异常传递给调用者或客户端之前进行的。

如果有未预料的unchecked exception,比如ArrayOutOfBoundsExceptionNullPointerException,容器仍然会回滚CMT事务,但是在这种情况下,容器会认为bean处在了一种inconsistent的情况下,会进而销毁bean实例,这是十分消耗资源的,所以you should never delibrately use system exceptions.

 

CMT使开发人员不必关心EJB事务中的大部分细节,当然同时CMT能提供对事务的控制级别也更少,这一点Bean管理的事务(BMT)更适合。

 

posted @ 2009-06-06 21:11 J@mes 阅读(902) | 评论 (0)编辑 收藏

2009年5月29日

作为近期学习EJB3  的一些心得和总结,完全是自己的理解和白话,前辈们请多指教

EJB3学习总结(1)

现状

EJB2得到了较广泛的应用,但真正用对场合的项目不多,那些强调分布式,即业务逻辑和Web是在分布在不同物理层的大型项目。更多得是使用在中小型web应用之上。

EJB3SpringHibernate等一系列轻量级框架运动的发展后现身了,EJB3是基于POJO组件的,同时提供了事务、安全、ORM和分布式等诸多特点,同时AOPDIAnnotation等特性也进一步提高了EJB3的易用性。

EJB3规范

EJB3规范包含3个技术文档:

1.       EJB3 Simplified API

2.       EJB3 Core Contracts & Requirement

3.       Java Persistence API

EJB3组件模型

1.       Session Bean:执行业务服务、控制事务及资源访问

2.       Message Driven Bean:异步调用,通过JMS关联消息队列(Queue)及Topic来响应外部事件

3.       Entity:具有唯一标示的实体,是持久化的基础。

SessionBeanMDBean统称为Enterprise Bean,这点不同于EJB2规范。EntityBean已经划分由持久化Provider去管理和控制,不再由EJB容器管理。

EJB3框架

它提供了对EJB3组件的各种支持,包括容器事务、安全服务、资源池的管理(包括线程池、连接池、实例池)以及组件生命周期的管理、并发支持等。

EJB3的核心features

1.       声明式的元数据:通过Java5AnnotationXML来声明式地去指定Enterprise BeanEntity Bean的行为和特性。如果同时使用两种方式时,XML描述具有更高的优先级。

2.       按异常配置:对于大量可使用default配置的地方都可以省去繁琐的配置,只有需要不按默认行为的才需要显式得通过注解或XML来进行描述。强调用户只有需要配置时才进行配置,可以使代码更为简洁。

3.       良好的可伸缩性:EJB3的实现中在三个方面保证了良好的伸缩性,(1)通过资源池最大程度上对重新对象的重用;(2)使用持久化及缓存避免重复查询和重复创建实体;(3)优化的锁定策略,避免对DB的并发锁定。

4.       JTAJava Transaction API)定义了分布式事务的标准APIEJB容器作为JTA的事务管理器。

5.       通过声明的方式来控制方法级别的访问控制,达到多层安全性。

6.       实体Bean被替换成POJO,简单、轻量,不用再去实现专门的接口,同时可以脱离EJB容器。

7.       SessionBean也更加灵活,不再需要主接口(Home Interface.

8.       依赖注入(dependency injection),可以通过AnnotationXML的方式将依赖数据“推(push)”到bean。例如:将EntityManager注入到SessionBean中,以使会话可以与持久化单元进行交互。

9.       拦截器和回调(Call-back):通过拦截器来完成某些回调方法。

10.   对于SessionBeanMDB,不在需要主方法(ejbCreate()),使用默认构造器来替代。同时也不需要再扩展专有接口。

11.   对于EntityBean,主接口(Home interface)也被替换成EntityManager,后者是一个单例实例工厂,可以管理实体Bean的生命周期。

12.   EJB3的分布式计算模型:EJB3也基于RMI远程服务,远程接口方法按值传递以提供粗粒度的模型。

EJB3角色

1.       定义Enterprise Bean及相关meta-data的三种角色:

(1)       企业Bean提供者(Enterprise Bean provider),负责去定义和实现业务逻辑和结构;负责定义实体的持久化结构及互相关系。

(2)       应用装配者(Application assembler)。

EJB3的会话Bean

EJB3中,SessionBean包括两种类型,Stateful SessionBeanStateless SessionBean

顾名思义,Stateless SessionBean不需维持客户请求的会话状态;而Stateful SessionBean则需要维持特定客户请求的会话状态,同时bean实例也是用客户请求绑定的。

Stateless SessionBean

无状态会话Bean由两个元素组成:业务接口,用来定义所提供的服务;bean类,是对服务接口的实现。注意此处,不需像EJB2.x中分别实现EJBObjectSessionBean接口。

通过示例,通过定义本地接口和(或)远程接口来定义业务接口,这里Local接口和Remote接口的选择遵循一个原则:如果业务的请求者与SessionBean处在同一个JVM中,则可以使用本地接口,反之则必须使用远程接口。

原则上,如果同时使用了本地接口和远程接口,则必须保证二者定义的接口一致,同时由实现的Bean实现这些方法。

无状态会话Bean无需实现EJB特定的接口或扩展类,只需在类级别使用注解——@Stateless即可。同时也在本地接口及远程接口的类级别添加注解——@Local@Remote

通过前面对EJB3特性的介绍,可以知道EJB3DIDependency Injection)的良好支持!在EJB3中,可以将各类资源注入到会话Bean中,这些资源可以是其他的会话Bean、数据源或者是JMS中的队列(Queue)等。要实现依赖注入可以通过添加注解,也可以在XML配置文件中进行描述,但需注意的是,如果二者都进行的配置则以XML文件中的描述为准。

以注解的方式为例,只需添加@Resource注解即可,注入可以通过两种方式:实例变量和setter方法上。

回调(Call-back),通过回调,可以对Bean在其生命周期内各个阶段进行更细粒度的控制和管理。使用回调方法也很简单,回调方法没有多余的限制,只需添加正确的注解即可。

无状态会话Bean两个主要的用于回调方法的注解分别是:@PostConstruct@PreDestroy。其中@PostConstruct的方法会在该bean被实例化后回调执行,但需要注意的是,如果该bean有配置了需要注入的资源,那该回调方法则会紧跟着资源的注入之后而执行。

@PreDestroy的回调方法则是在容器即将销毁bean实例之前被调用,主要用来做一些善后的工作,比如对资源的关闭和清理。(补充,在有状态会话Bean中,该方法是在最后一个带有@Remove注解的方法调用后才被调用,之后容器销毁bean实例。)

另一个关键的元素是拦截器(Interceptor),拦截器的使用也很便捷,通过添加正确的注解即可。拦截器的概念与其他JavaEE的框架或规范中的一致,即拦截业务方法的调用,可以在拦截点附加新的业务逻辑,结合依赖注入特性,可以充分得做到关注点分离(Separation of Concerns)。Enterprise Bean中会话Bean和消息驱动Bean可以定义拦截器方法。

拦截器注解可以添加到方法级别,也可以添加到类级别。被标注的方法在被调用时会被拦截器类拦截,并插队式的先去调用拦截器的方法。对于用到的拦截器类需要添加@Interceptor注解,如果有多个拦截器则使用@Interceptors

@AroundInvoke注解为例,拦截器方法需要关注InvocationContext接口,通过它可以获得被拦截的bean类(Class)、bean中的方法(Method)等,需要强调的是其中的proceed()方法,通过它将拦截请求往后传递,或者到拦截器链中的下一个,或者是结束拦截调用真正的bean方法。

EJB3规范中定义了两种类型的异常,分别是应用异常和系统异常。应用异常是业务逻辑中产生的checked exception;而系统异常则是EJB系统级产生的异常,同时系统异常都是RemoteExceptionRuntimeException的子类,是unchecked exception

有状态会话BeanStateful SessionBean)在特性及细节上与无状态会话Bean很相似。

会话Bean的用户视图

访问会话Bean的用户视图可以有三种形式:

1.       通过Remote接口,远程客户具有位置无关性。

2.       通过Local接口,这两种方式中请求方可以是其他的EJB组件,可以是ServletJSP等。需要注意的是,本地客户具有位置依赖性。

3.       WebService方式,可以将会话Bean发布成为一个WebService,供客户调用。

客户请求会话Bean时,或者通过依赖注入或者通过查找JNDI,来获得会话Beanstub对象,请求是通过stub来进行调用的。对于无状态会话Bean,每次请求将获得新的stub,而有状态会话Bean,则在请求方缓存stub,这样才能使容器知道该返回哪个与客户相关联的bean实例。

通过依赖注入获得会话Bean业务接口的方式是添加注解@EJB,注意要与@Resource区分开。

相比查找JNDI,使用注入的方式会更加简洁,通常对于远程请求使用JNDI更适合。

有状态会话BeanStateful SessionBean

通过实现SessionSynchronization接口,可以在事务点上获得EJB容器的通知:afterBegin,在新事物开始时;beforeCompletion,在事物提交前;afterCompletion,在事物执行完之后。

有状态会话Bean中的回调方法除了PostConstructPreDestroy外,还有PreActivatePrePassivate,分别使用@PreActivate@PrePassivate来注解。

前两个回调方法的细节与无状态会话Bean一致,分别在(1)实例化Bean之后并执行完资源注入后执行;(2@Remove方法执行完毕之后。

对于有状态会话Bean中的@Remove方法,也是一个管理bean生命周期的方法,调用该方法后,容器将会从实例池中将该bean删除。

带有@PrePassivate注解的方法会由EJB容器调用,当某个有状态会话Bean实例长时间空闲,则容器调用该方法将此bean实例钝化,并将状态缓存起来。

当客户请求再次需要使用被钝化的某bean实例时,容器调用该bean@PreActivate方法,返回一个创建好的并带有状态的新实例。

有状态会话Bean的拦截器方法需要注意的是,如果实现SessionSynchronization接口的beanafterBegin始终发生在@AroundInvoke的任何方法前。

posted @ 2009-05-29 22:28 J@mes 阅读(736) | 评论 (0)编辑 收藏

仅列出标题