EJB3的事务与安全
EJB3的是事务也符?/span>ACIDQ即原子性、一致性、隔d、持久性。这些特性与数据库事务一_需要强调的是一致性,在事务开始前Q系l是处于一U遵守业务规则和U束的一致状态下Q那么在事务提交或回滚之后,pȝ也必ȝ持这U一致性状态。在事务q行q程中不必处于不一致性状态,事务在这里就像一个沙(sand-boxQ?/span>
?/span>EJB中,事务也具有隔ȝ别的控制Q但一般不用通过EJB容器来控Ӟ而是在数据库资源q一U别来进行控制?/span>
要知道在EJB容器中,代码层面的所有操作最l都是{化ؓ两的数据库操作Q比如锁定和解锁数据库中的某行或某张表。事务日志来反映事务的变化,开始事务日志代表事务的开始,应用日志代表以提交该事务来结束,相反攑ּ日志׃表回滚事务而结束?/span>
资源理器(Resource ManagerQ的概念是管理特定某U资源的事务的组件。这个概念不仅包括关pL据库pȝQ也可以使消息服务器或其他业务系l?/span>
如果只涉及一U单一资源的事务就UCؓlocal transaction本地事务Q相反大多数企业应用都是需要涉及多U不同的资源。这时候就需要一U类型的lg来管理事务中多种资源Q这个组件称Z务管理器Q?/span>Transaction ManagerQ,它在多个理各自资源事务的资源管理之间进行协调和控制?/span>
分布式的事务理是通过一U称ZD式提交Q?/span>two-phase commitQ的机制来完成。现在最为流行的分布式事务协议是XA协议Q?/span>XA ProtocolQ,JavaEE是通过该协议来完成分布式应用中的事务管理?/span>
事务理
JavaEE中事务?/span>JTAQ?/span>Java Transaction APIQ是建立?/span>Java Transaction Service之上的服务)Q即javax.tranaction.UserTransaction接口Q容器在后台会自动管理大多数事务l节Q?/span>EJB开发者只需控制开始和停止事务、徏立事务边界(transaction boundaryQ以及是否提?/span>/回滚业务卛_?/span>
?/span>EJB3中提供了两种具体的事务方式,卛_器管理的事务Q?/span>Container-managed transactionQ?/span>CMTQ和bean理的事务(Bean-managed transactionQ?/span>BMTQ。其中前者是使用声明式地或通过部v描述W来理事务Q后者需要以昑ּ~码的方式来理。但需要注意的是在EJB3中,只有SessionBean?/span>MessageDrivenBean才支?/span>CMT?/span>BMTQ?/span>JPA中ƈ不直接依?/span>CMT?/span>BMT。但是当Ӟ在Q?/span>JavaEE容器中都可透明得插?/span>CMT?/span>BMT的事务环境?/span>
EJB3中管理事务最单灵zȝ方式是采用容器管理事务,?/span>CMT?/span>
容器理的事务,思义Q容器来?#8220;事务开始、事务提交或回滚”。容器在调用Ҏ前开?/span>JTA事务Q接着会调用业务方法,最后根据调用中发生的情况去军_是提交事务还是回滚事务。?/span>CMT事务理Q只需x@TransactionManagement?/span>@TransactionAttribute注解Q同旉过EJBContext的方法来回滚事务?/span>
1Q?/span>@TransactionMangement注解用于向容器标识该bean的事务管理是使用CMTq是BMTQ是通过该标{中?/span>value属性来指明TransactionManagementType的枚丑ր{?/span>
2Q?/span>@TransactionAttribute注解Q尽由容器为我们来理事务的各U细节,但是仍然需要通过该注解来告知容器如何去自动管理?/span>
CMT对事务的理可以在包?/span>bean的方法时开始,也可以从调用者的事务中进?/span>join开始?/span>
TransactionAttributeType有几个枚丑ր|
Q?Q?span style="font: 7pt 'Times New Roman'"> REQUIREDQ如果调用者没有事务,容器则创建新事务Q如果调用者具有事务,容器则连?/span>join调用者事务?/span>
Q?Q?span style="font: 7pt 'Times New Roman'"> REQUIRED_NEWQ如果调用者没有事务,容器创建新事务Q如果调用者有事务Q容器暂停原来事务,q创建新的事务?/span>
Q?Q?span style="font: 7pt 'Times New Roman'"> SUPPORTSQ如果调用者没有事务,容器׃创徏事务Q反之则joinq用调用者的事务?/span>
Q?Q?span style="font: 7pt 'Times New Roman'"> MANDATORYQ如果调用者没有事务,容器抛出javax.ejb.EJBTransactionRequiredException异常Q反之则q接q用调用者的事务?/span>
Q?Q?span style="font: 7pt 'Times New Roman'"> NOT_SUPPORTEDQ如果调用者没有事务,容器便不使用事务调用ҎQ反之则暂停调用者事务以non-transactional的方式调用方法?/span>
Q?Q?span style="font: 7pt 'Times New Roman'"> NEVERQ如果调用者没有事务,容器直接调用ҎQ反正抛?/span>javax.ejb.EJBException异常?/span>
a. 对于W?/span>1U情况,适合的场景是?/span>non-transactional?/span>Web层调?/span>beanҎQ如果方法调用出现异常,那么容器不仅?/span>rollback整个事务Q同时也会向调用者抛?/span>javax.transaction.RollbackException异常Q以通知调用者事务已被回滚了?/span>
b. W?/span>2U情况中Q始l需要容器去创徏新的事务Q对于调用者原有的事物Q容器就会暂停它Q直到方法调用返回ؓ止就恢复原有事务。这Ll果是Ҏ调用所创徏的新事务无论成功与否Q都不会对调用者原有的事务造成影响?/span>
c. SUPPORT选项表示本质上容器将会按照调用者的事务情况来处理,通常的场景是用于一些只d的操作,比如索数据记录等?/span>
d. MANDATORY表示对容器对事务的强制性要求,如果调用者存在事务,容器?/span>joinq用调用者事务;但如果调用者没有事务环境,׃抛出EJBTransactionRequiredException异常。这U选项适合于如果方法调用失败进行回滚时也保证调用者环境的p|?/span>
e. 对于NOT_SUPPORTQ容器处理的本质是要求不能在事务环境下调?/span>beanҎ。如果调用者存在事务,容器会先暂停它,然后开始方法调用,在返回后恢复调用者的事务?/span>
f. NEVER表明?/span>CMT中,容器不能允许从事务性环境中调用beanҎQ否则会抛出EJBException异常?/span>
对于前面q六U?/span>TransactionAttributeTypeQ?/span>MDBq不是全部都支持Q它支持REQUIRED?/span>NOT_SUPPORT两种。这一点与MDB的特性有养I客户端无法直接调?/span>MDBQ所以事务属性中?/span>SUPPORT?/span>REQUIRED_NEW?/span>MANDATORY没有意义?/span>
CMT事务理的真正机?/span>
很重要的一点:CMTҎ要求容器回滚事务q不是立卌行的Q而是向容器设|回滚标识,在事务结束的时候,容器查该标志Q如果不需回滚则提交该事务Q如果需要回滚则q行?/span>
通过EJBContext或其子类Q?/span>SessionContext?/span>MessageDrivenContext?/span>setRollbackOnly()ҎQ将回滚标志位置?/span>trueQ当Ҏ调用l束Ӟ容器查该状态以判断是否提交事务或是回滚事务?/span>
需要注意的一ҎQ本质上Q?/span>EJBContextq行讄标志位时Q它是作为底层事务的一个上层抽象代理。所以用时Q必M证有底层事务存在Q也是说必dREQUIRED,REQUIRED_NEW,MADATORYq三U事务属性下使用Q因三种事务属性都可以使容器保证底层事务的存在Q即无论Ҏ调用者是否存在事务环境,容器都会创徏C务?/span>
?/span>setRollbackOnly()Ҏ对应的是getRollbackOnly()ҎQ该Ҏq回当前EJBContext中回滚标志位的状态。该Ҏ在业务处理中十分有用Q考虑q样一个场景:
在进行一D非帔R的资源密集型操作前如果前事务的前部分已经p|Q那是在一个注定要回滚的事务上付出很多代h。所以在q类操作之前应该?/span>check一?/span>rollbackOnly是否已经|ؓtrue?/span>
此外Q处理该标志位状态的代码l常分布在业务逻辑?/span>catch块中Q如?/span>catch了某U?/span>exceptionQ则一边记录进logQ一辚w过setҎ|状态位。在EJB3中,可以通过@ApplicationException注解使得“捕获异常转化Z务回?#8221;变成透明的机制?/span>
@AplicationException注解
@javax.ejb.ApplicationException该注解可以用来控制事务性输出,要抛出的异常类型在定义时加上该标签Q同时设|该标签?/span>rollback属性,?/span>@ApplicationException(rollback=true/false)。应用异常(ApplicationExceptionQ是希望调用者或客户端处理的Q一般认为除?/span>java.rmi.RemoteException?/span>java.lang.RuntimeException之外都是应用异常。而从上述两种异常l承出的子类异常都被认ؓ是系l异常,q种异常不会传递给调用者或客户端而是wrapped?/span>EJBException中?/span>
d此注解的异常无论?/span>checked exceptionq是unchecked exceptionQ比如运行时异常Q,都会被认为是应用异常Q?/span>application exceptionQ,从而传递给Ҏ调用者或客户端?/span>
默认情况下,所?/span>checked exception或所有被注解为应用异常的checked/unchecked exceptionQ都不会?/span>CMT的方式回滚,如果?/span>rollback属性设|ؓtrueQ则会通知容器p|时进行事务回滚,q且是在把应用异怼递给调用者或客户端之前进行的?/span>
CMT使开发h员不必关?/span>EJB事务中的大部分细节,当然同时CMT能提供对事务的控制别也更少Q这一?/span>Bean理的事务(BMTQ更适合?/span>
EJB3学习ȝQ?Q?/span>
现状
EJB2得到了较q泛的应用,但真正用对场合的目不多Q那些强调分布式Q即业务逻辑?/span>Web是在分布在不同物理层的大型项目。更多得是用在中小?/span>web应用之上?/span>
EJB3?/span>Spring?/span>Hibernate{一pd轻量U框架运动的发展后现w了Q?/span>EJB3是基?/span>POJOlg的,同时提供了事务、安全?/span>ORM和分布式{诸多特点,同时AOP?/span>DI?/span>Annotation{特性也q一步提高了EJB3的易用性?/span>
EJB3规范
EJB3规范包含3个技术文:
1. EJB3 Simplified API
2. EJB3 Core Contracts & Requirement
3. Java Persistence API
EJB3lg模型
1. Session BeanQ执行业务服务、控制事务及资源讉K
2. Message Driven BeanQ异步调用,通过JMS兌消息队列Q?/span>QueueQ及Topic来响应外部事?/span>
3. EntityQ具有唯一标示的实体,是持久化的基?/span>
SessionBean?/span>MDBeanl称?/span>Enterprise BeanQ这点不同于EJB2规范?/span>EntityBean已经划分由持久化Providerȝ理和控制Q不再由EJB容器理?/span>
EJB3框架
它提供了?/span>EJB3lg的各U支持,包括容器事务、安全服务、资源池的管理(包括U程池、连接池、实例池Q以及组件生命周期的理、ƈ发支持等?/span>
EJB3的核?/span>features
1. 声明式的元数据:通过Java5?/span>Annotation?/span>XML来声明式地去指定Enterprise Bean?/span>Entity Bean的行为和Ҏ。如果同时用两U方式时Q?/span>XML描述h更高的优先?/span>
2. 按异帔R|:对于大量可?/span>default配置的地斚w可以省去J琐的配|,只有需要不按默认行为的才需要显式得通过注解?/span>XML来进行描q。强调用户只有需要配|时才进行配|,可以使代码更为简z?/span>
3. 良好的可伸羃性:EJB3的实C在三个方面保证了良好的~性,Q?/span>1Q通过资源池最大程度上寚w新对象的重用Q(2Q用持久化及缓存避免重复查询和重复创徏实体Q(3Q优化的锁定{略Q避免对DB的ƈ发锁定?/span>
4. JTAQ?/span>Java Transaction APIQ定义了分布式事务的标准API?/span>EJB容器作ؓJTA的事务管理器?/span>
5. 通过声明的方式来控制ҎU别的访问控Ӟ辑ֈ多层安全性?/span>
6. 实体Bean被替换成POJOQ简单、轻量,不用再去实现专门的接口,同时可以qEJB容器?/span>
7. SessionBean也更加灵z,不再需要主接口Q?/span>Home InterfaceQ?/span>.
8. 依赖注入Q?/span>dependency injectionQ,可以通过Annotation?/span>XML的方式将依赖数据“推(pushQ?#8221;?/span>bean。例如:?/span>EntityManager注入?/span>SessionBean中,以会话可以与持久化单元q行交互?/span>
9. 拦截器和回调Q?/span>Call-backQ:通过拦截器来完成某些回调Ҏ?/span>
10. 对于SessionBean?/span>MDBQ不在需要主ҎQ?/span>ejbCreate()Q,使用默认构造器来替代。同时也不需要再扩展专有接口?/span>
11. 对于EntityBeanQ主接口Q?/span>Home interfaceQ也被替换成EntityManagerQ后者是一个单例实例工厂,可以理实体Bean的生命周期?/span>
12. EJB3的分布式计算模型Q?/span>EJB3也基?/span>RMIq程服务Q远E接口方法按g递以提供_粒度的模型?/span>
EJB3角色
1. 定义Enterprise Bean及相?/span>meta-data的三U角Ԍ
Q?Q?span style="font: 7pt 'Times New Roman'"> 企业Bean提供者(Enterprise Bean providerQ,负责d义和实现业务逻辑和结构;负责定义实体的持久化l构及互相关pR?/span>
Q?Q?span style="font: 7pt 'Times New Roman'"> 应用装配者(Application assemblerQ?/span>
EJB3的会?/span>Bean
EJB3中,SessionBean包括两种cdQ?/span>Stateful SessionBean?/span>Stateless SessionBean?/span>
思义Q?/span>Stateless SessionBean不需l持客户h的会话状态;?/span>Stateful SessionBean则需要维持特定客戯求的会话状态,同时bean实例也是用客戯求绑定的?/span>
Stateless SessionBean
无状态会?/span>Bean׃个元素组成:业务接口Q用来定义所提供的服务;beanc,是对服务接口的实现。注意此处,不需?/span>EJB2.x中分别实?/span>EJBObject?/span>SessionBean接口?/span>
通过CZQ通过定义本地接口和(或)q程接口来定义业务接口,q里Local接口?/span>Remote接口的选择遵@一个原则:如果业务的请求者与SessionBean处在同一?/span>JVM中,则可以用本地接口,反之则必M用远E接口?/span>
原则上,如果同时使用了本地接口和q程接口Q则必须保证二者定义的接口一_同时由实现的Bean实现q些Ҏ?/span>
无状态会?/span>Bean无需实现EJB特定的接口或扩展c,只需在类U别使用注解—?/span>@Stateless卛_。同时也在本地接口及q程接口的类U别d注解—?/span>@Local?/span>@Remote?/span>
通过前面?/span>EJB3Ҏ的介绍Q可以知?/span>EJB3?/span>DIQ?/span>Dependency InjectionQ的良好支持Q在EJB3中,可以各c资源注入到会话Bean中,q些资源可以是其他的会话Bean、数据源或者是JMS中的队列Q?/span>QueueQ等。要实现依赖注入可以通过d注解Q也可以?/span>XML配置文g中进行描qͼ但需注意的是Q如果二者都q行的配|则?/span>XML文g中的描述为准?/span>
以注解的方式ZQ只需d@Resource注解卛_Q注入可以通过两种方式Q实例变量和setterҎ上?/span>
回调Q?/span>Call-backQ,通过回调Q可以对Bean在其生命周期内各个阶D进行更l粒度的控制和管理。用回调方法也很简单,回调Ҏ没有多余的限Ӟ只需d正确的注解即可?/span>
无状态会?/span>Bean两个主要的用于回调方法的注解分别是:@PostConstruct?/span>@PreDestroy。其?/span>@PostConstruct的方法会在该bean被实例化后回调执行,但需要注意的是,如果?/span>bean有配|了需要注入的资源Q那该回调方法则会紧跟着资源的注入之后而执行?/span>
@PreDestroy的回调方法则是在容器卛_销?/span>bean实例之前被调用,主要用来做一些善后的工作Q比如对资源的关闭和清理。(补充Q在有状态会?/span>Bean中,该方法是在最后一个带?/span>@Remove注解的方法调用后才被调用Q之后容器销?/span>bean实例。)
另一个关键的元素是拦截器Q?/span>InterceptorQ,拦截器的使用也很便捷Q通过d正确的注解即可。拦截器的概念与其他JavaEE的框架或规范中的一_x截业务方法的调用Q可以在拦截炚w加新的业务逻辑Q结合依赖注入特性,可以充分得做到关注点分离Q?/span>Separation of ConcernsQ?/span>Enterprise Bean中会?/span>Bean和消息驱?/span>Bean可以定义拦截器方法?/span>
拦截器注解可以添加到ҎU别Q也可以d到类U别。被标注的方法在被调用时会被拦截器类拦截Qƈ插队式的先去调用拦截器的Ҏ。对于用到的拦截器类需要添?/span>@Interceptor注解Q如果有多个拦截器则使用@Interceptors?/span>
?/span>@AroundInvoke注解ZQ拦截器Ҏ需要关?/span>InvocationContext接口Q通过它可以获得被拦截?/span>beanc(ClassQ?/span>bean中的ҎQ?/span>MethodQ等Q需要强调的是其中的proceed()ҎQ通过它将拦截h往后传递,或者到拦截器链中的下一个,或者是l束拦截调用真正?/span>beanҎ?/span>
EJB3规范中定义了两种cd的异常,分别是应用异常和pȝ异常。应用异常是业务逻辑中生的checked exceptionQ而系l异常则?/span>EJBpȝU生的异常Q同时系l异帔R?/span>RemoteException?/span>RuntimeException的子c,?/span>unchecked exception?/span>
有状态会?/span>BeanQ?/span>Stateful SessionBeanQ在Ҏ及l节上与无状态会?/span>Bean很相伹{?/span>
会话Bean的用戯?/span>
讉K会话Bean的用戯囑֏以有三种形式Q?/span>
1. 通过Remote接口Q远E客户具有位|无x?/span>
2. 通过Local接口Q这两种方式中请求方可以是其他的EJBlgQ可以是Servlet?/span>JSP{。需要注意的是,本地客户h位置依赖性?/span>
3. WebService方式Q可以将会话Bean发布成ؓ一?/span>WebServiceQ供客户调用?/span>
客户h会话BeanӞ或者通过依赖注入或者通过查找JNDIQ来获得会话Bean?/span>stub对象Q请求是通过stub来进行调用的。对于无状态会?/span>BeanQ每ơ请求将获得新的stubQ而有状态会?/span>BeanQ则在请求方~存stubQ这h能容器知道该返回哪个与客户相关联的bean实例?/span>
通过依赖注入获得会话Bean业务接口的方式是d注解@EJBQ注意要?/span>@Resource区分开?/span>
相比查找JNDIQ用注入的方式会更加简z,通常对于q程h使用JNDI更适合?/span>
有状态会?/span>BeanQ?/span>Stateful SessionBeanQ?/span>
通过实现SessionSynchronization接口Q可以在事务点上获得EJB容器的通知Q?/span>afterBeginQ在C物开始时Q?/span>beforeCompletionQ在事物提交前;afterCompletionQ在事物执行完之后?/span>
有状态会?/span>Bean中的回调Ҏ除了PostConstruct?/span>PreDestroy外,q有PreActivate?/span>PrePassivateQ分别?/span>@PreActivate?/span>@PrePassivate来注解?/span>
前两个回调方法的l节与无状态会?/span>Bean一_分别在(1Q实例化Bean之后q执行完资源注入后执行;Q?/span>2Q?/span>@RemoveҎ执行完毕之后?/span>
对于有状态会?/span>Bean中的@RemoveҎQ也是一个管?/span>bean生命周期的方法,调用该方法后Q容器将会从实例池中该bean删除?/span>
带有@PrePassivate注解的方法会?/span>EJB容器调用Q当某个有状态会?/span>Bean实例长时间空Ԍ则容器调用该Ҏ此bean实例钝化Qƈ状态缓存v来?/span>
当客戯求再ơ需要用被钝化的某bean实例Ӟ容器调用?/span>bean?/span>@PreActivateҎQ返回一个创建好的ƈ带有状态的新实例?/span>
有状态会?/span>Bean的拦截器Ҏ需要注意的是,如果实现SessionSynchronization接口?/span>beanQ?/span>afterBegin始终发生?/span>@AroundInvoke的Q何方法前?/span>