【JavaEE】经典JAVA EE企业应用实战-读书笔记11

来源:互联网 发布:身份证psd源码 编辑:程序博客网 时间:2024/06/07 18:34

Bean管理事务,主要就是通过UserTransaction来控制事务开始、结束。

首先需要获得UserTransaction,有三种方式

1)使用@Resource执行依赖注入

2)通过JNDI查找来获取,代码如下

UserTransaction tx=(UserTransaction)ctx.lookup(“UserTransaction”);

3)使用EJBContextgetUserTransaction方法,代码如下

UserTransaction tx=ctx.getUserTransaction();

其中第一中比较简单,接着在代码使用

tx.begin();tx.commit();tx.rollback();

 

下面是UserTransaction所包含方法的简单解释

begin:用于开始新的事务,创建一个新的事务,并将他关联到当前执行线程中。

commit:用于提交事务,将会结束当前线程关联的事务

getStatus:用于获取当前线程关联的事务状态

rollback:用于回滚事务,同样结束当前线程关联的事务

setRollbackOnly:只能在CMT事务管理器中使用,用于通知容器回滚当前事务。

setTransactionTimeout(int secondes):用于修改当前线程所关联事务的超时时长

上面的方法中getStatusEJBContextgetRollbackOnly方法的复杂版,getRollbackOnly只能返回一个boolean值,而getStatus方法则返回代表当前事务状态的值,包括

javax.transaction.Status.STATUS_ACTIVE:当前线程处于活动阶段

javax.transaction.Status.STATUS_COMMITTED:当前线程已经被提交

javax.transaction.Status.STATUS_COMMITTING:当前线程正处于提交过程中

javax.transaction.Status.STATUS_MARKED_ROLLBACK:当前线程已被标记为回滚。可能是由于当前线程调用了UserTransactionsetRollback(true)方法。

javax.transaction.Status.STATUS_NO_TRANSACTION:当前线程没有事务支持

javax.transaction.Status.STATUS_PREPARED:当前事务正准备提交,正在等待从属资源的响应

javax.transaction.Status.STATUS_PREPARING:当前事务处于预备阶段,所有从属资源都已经同意提交

这两个状态值与两段式提交有关,两段式提交协议方式在正式提交事务之前,需要先尝试提交该事务的多个从属源,只有当从属源尝试提交都返回成功时,JTA全局事务才会正式提交。在第一阶段中尝试提交了多个从属资源,当这些从属资源的结果尚未返回时,事务处于STATUS_PREPARING状态;当这些从属资源都返回了提交成功时,这意味着JTA全局事务可以真正提交了,那么事务处于STATUS_PREPARED状态。

javax.transaction.Status.STATUS_ROLLEDBACK:当前事务已经被回滚了

javax.transaction.Status.STATUS_ROLLING_BACK:当前事务处于回滚过程中

javax.transaction.Status.STATUS_UNKNOWN:当前事务处于未知状态

EJB容器建议使用CMT,因此CMT也是默认配置。因为BMT需要开发者硬编码来实现,导致业务逻辑和事务逻辑混杂,也导致了Session Bean的事务逻辑难以切换成其他管理方式。BMT还有一个严重问题,当客户端调用BMT方法时,总会暂停当前已有事务,制约了组件的重用。

 

EJB3提供了拦截器支持,但是只提供了一个@AroundInvoke。用于修饰EJB3中的拦截器方法。

public class MyInterceptor {@AroundInvokepublic Object log(InvocationContext ctx) throws Exception {System.out.println("interceptor start");// 让目标方法执行Object rvt = ctx.proceed();if (rvt != null) {rvt = "interceptor change return value " + rvt;}System.out.println("interceptor end");return rvt;}}

需要注意的是@AroundInvoke修饰的方法必须满足如下格式

public Object xxx(InvocationContext ctx)throws Exception

InvocationContext 对象中包含如下方法

1)Map<String,Object> getContextData()Map对象里封装了本次调用或生命周期回调相关的上下文信息

2)Method getMethod():获取被拦截的方法

3)Object[] getParameters():获取被拦截的业务方法的实际参数

4)Object getTarget():获取被拦截的Session Bean实例

5)Object proceed():调用InvocationContext的该方法是就是回调拦截方法、执行被拦截方法

6)void setParameters():修改被拦截的业务方法的实际参数

 

在需要被拦截的Bean实现类使用@Interceptors(MyInterceptor.class)来修饰类或业务方法。

如果修饰类的话,对类中所有业务方法都起作用,但是可以使用@ExcludeClassInterceptors来排除某个方法不使用拦截器。

 

EJB注入只要靠位于javax.ejb包下@EJB@EJBs两个注解来提供。@EJB即可用于修饰Bean实现类的成员变量,也可用于修饰Bean类的setter方法

使用@EJB是可指定如下属性

1)beanInterface:指定被注入EJB所实现的接口。通常用于区分所引用的Bean是远程调用Bean还是本地Bean

2)name:指定被注入BeanJNDI ENC中的注册项名称,该名称在不同应用服务器中可能存在差异。

3)beanName指定被注入EJB的名称。其值与被注入Bean@Stateless.name@Stateful.nameejb-jar.xml中的<ejb-name>元素所指定的值相等。

4)mappedName:指定被注入EJBJNDI名,但由于全局JNDI名与应用服务器厂商有关系,因此设置改属性可能降低应用的可移植性。

ENC的全称是Enterprise Naming Context,是应用服务器的一个内部注册表。

前面说本地调用EJBWebLogic服务器中不会被暴露出来,只能被位于同一个应用中的其他组件调用,此处将会介绍通过EJB注入将本地调用的EJB注入远程调用的EJB中,从而间接地让本地EJB也可以对外提供服务。

@Localpublic interface Hello{  public String hello(String name);}@Stateless(name=”Hello”)public class HelloBean implements Hello{  public String hello(String name){    return “hello” + name + new java.util.Date();  }}@Remotepublic interface CallHello{  String callHello(String name);}@Stateless(mappedName=”CallHello”)public class CallHelloBean implements CallHello{  @EJB(beanName=”Hello”)  private Hello hello;  public String callHello(String name){    final String prefix=”调用Hello EJB成功,返回”;    String result=hello.hello(“kingdz”);    System.out.println(prefix+result);    return prefix+result;  }}

通常使用@Resource来实现资源的注入,用法和@EJB类似。

@Resource可使用如下几个属性

1)String mappedName:指定该资源的JNDI引用名称

2)String name:该属性指定外部资源的JNDI ENC名称,该名称在不同的应用服务器中可能会有变化

3)boolean shareable:该属性指定该资源是否可以共享

4)Class type:该属性指定该资源对应的Java类名

例如:

@Resource(mappedName=”javaee”)

private DataSource ds=null;

@Resource

private SessionContext sessCtx;

 

配置EJB引用

除了依赖注入,还可以使用EJB2的形式,在ejb-jar.xml部署描述文件中进行配置。

通过在<session>元素中添加如下两个元素

<ejb-ref>:为一个Remote EJB配置EJB引用

<ejb-local-ref>:为一个Local EJB配置EJB引用

上面两个元素都用于为一个Session Bean配置EJB引用,都可以添加如下子元素:

<ejb-ref-name>:用于为EJB配置一个名称

<ejb-ref-type>:指定所引用EJB的类型,在EJB3规范中该属性的值通常是Session

<local><remote>:用于指定EJB业务接口的全限定类名

例如

<session>

<!--指定为哪个EJB配置EJB引用-->

<ejb-name>EJBRef</ejb-name>

<ejb-local-ref>

<!--指定EJB引用的名称-->

<ejb-ref-name>ejb3/Hello</ejb-ref-name>

<ejb-ref-type>Session</ejb-ref-type>

<!--指定所引用EJB所实现的业务接口-->

<local>com.kingdz.service.Hello</local>

</ejb-local-ref>

</session>

此处配置的EJB引用名可能并不一定就是完整的JNDI绑定名。

 

EJB3的定时器服务由TimerService对象代表,调用createTimer方法,有如下几个参数

1)Date initialExpiration:指定定时器将于指定的initialExpiration时刻触发

2)long intervalDuration:指定定时器的触发时间间隔为intervalDuration毫秒

3)Serializable info:指定一个描述性信息

 

调用createTimer方法之后,会为该EJB创建一个定时器对象,就会控制EJB内被@Timeout修饰的方法在指定时间执行,@Timeout修饰的方法必须满足如下签名

public void xxx(Timer timer);

@Timeout修饰的方法不应该由客户端来调用,应该由定时器负责调用。

Timer对象提供了如下方法来访问定时器

1)void cancel:取消定时器

2)TimerHandle getHandle:获取定时器的引用

3)Serializable getInfo:获取定时器的描述性信息。就是调用createTimer方法通过info参数传入的信息

4)Date getNextTimeout:获取该定时器下一次触发的时刻

5)long getTimeRemaining:获取定时器还需多少毫秒将会触发下一次任务调度

@Remotepublic interface TimerEJB {void setTime(Date init, long interval);void check(Timer timer);}@Stateless(mappedName = "TimerEJB")public class TimerEJBBean implements TimerEJB {@ResourceTimerService timerService;@Override@Timeoutpublic void check(Timer timer) {System.out.println(timer.getInfo());System.out.println("mock system check");}@Overridepublic void setTime(Date init, long interval) {timerService.createTimer(init, interval, "new timer");}}
Timer对象提供了一个getHandle方法获取该定时器对应的TimerHandler对象。而TimerHandler是可序列化的,但该对象不应该通过网络传输,也就是说他不能传给客户端,即远程客户端不能访问EJBTimerHandler对象。
0 0