第 2 章 EJB 知识与运行环境设置( 2 )
(上接第 2 章的第 1 部分)
16. 改变 JNDI 名称
1) 对于 JBoss 应用服务器,可以使用 JBoss 提供的 @LocalBinding 和 @RemoteBinding 注释来指定会话 Bean 的 JNDI 名称。例如:
@RemoteBinding(jndiBinding=”foshanShop/RemoteOperation”)
@LocalBinding(jndiBinding=”foshanShop/LocalOperation”)
2) 对于 WebLogic 、 Sun Application Server 、 Glassfish 等应用服务器,可以使用 @Stateless 的 mappedName() 属性设置 Bean 的全局 JNDI 名称。例如:
@Stateless(mappedName=”OperationBeanRemote”)
3) 为了避免 JNDI 名称与厂商相关而造成的移植问题,建议使用 ejb-jar.xml 部署描述文件进行定义。该文件必须置于 jar 的 META-INF 目录下。
17. 会话 Bean 的生命周期事件
1) @PostConstruct :
适用于有状态和无状态会话 Bean 。当 Bean 实例化完成后,标注了这个注释的回调方法会被立即调用,且仅此一次调用。每个 Bean 类只能定义一个 @PostConstruct 方法。
2) @PreDestroy
适用于有状态和无状态会话 Bean 。当容器销毁一个无用的或者超时的 Bean 实例时,标注了这个注释的回调方法会被调用,且仅此一次调用。每个 Bean 类只能定义一个 @ PreDestroy 方法。
3) @PrePassivated
适用于有状态会话 Bean 。当 Bean 实例被钝化之前,具有该注释的回调方法会被调用。如果钝化期间,客户端仍然长期没有对 Bean 实例进行操作,则容器会从硬盘上删除它。此后,任何针对这个 Bean 实例方法的调用,都会导致异常。
4) @PostActivated
适用于有状态会话 Bean 。当 Bean 实例从钝化状态被激活时,容器会重新实例化一个新的 Bean 实例,并从硬盘恢复钝化之前的状态。完成激活之后,具有该注释的回调方法会被调用。
5) @Init
用于指定有状态会话 Bean 的初始化方法。其与 @PostConstruct 的区别在于,有状态会话 Bean 中可以存在多个 @Init 标注的方法。 @PostConstruct 在 @Init 之后被调用。
6) @Remove
当客户端调用了标注为 @Remove 的方法时,容器会在方法执行结束后,删除 Bean 实例。
7) 对于上面的注释方法,必须是 void ,不带参数的,不抛检查异常的方法。
8) @PostActivate 与 @PostConstruct , @PrePassivate 与 @PreDestroy 之间实质上并无很大差别。
l @PostActivate 和 @PostConstruct 通常都应该用于初始化或恢复无法序列化的资源,比如数据库连接;
l @PrePassivate 和 @PreDestroy 通常都应该用于关闭无法序列化的资源。因为进入钝化状态后,当 Bean 实例超时,容器在清除 Bean 实例之前,是不会调用 @PreDestroy 方法关闭尚处于打开状态的资源的,比如数据库连接。
18. 拦截器用于封装应用的公用行为,可以拦截会话 Bean 和消息驱动 Bean 的方法调用或生命周期事件。
19. 开发拦截器
1) @AroundInvoke 注释
l 该注释用于指定用作拦截器的方法。拦截器方法与被拦截的业务方法在同一个 Java 调用堆栈、同一个事务和安全上下文中。
l 用 @AroundInvoke 标识的方法必须遵守以下格式:
public Object methodName(javax.interceptor.InvocationContext ctx) throws Exception
2) InvocationContext
此上下文接口封装了关于客户端所调用业务方法的一些信息:
l getTarget() :返回 Object ,指向被调用的 Bean 实例。
l getMethod() :返回 Method ,指向被拦截的业务方法。
l getParameters() :返回 Object[] 数组,指向被拦截业务方法的参数。
l setParameters() :返回 void ,设置被拦截业务方法的参数。
l getContextData() :返回 Map<String, Object> ,在整个方法调用期间都可以访问。同一方法调用内的不同拦截器之间可以利用这个 Map 来传递上下文相关的数据。
3) proceed 方法,返回 Object
l InvocationContext 的 proceed() 方法内部会继续调用后面拦截器的 @AroundInvoke 方法,直到所有的拦截方法全都被调用以后,才会执行被拦截的业务方法。(个人感觉,有点像 FilterChain 的 doFilter 方法)
l proceed() 方法必须在拦截器代码中调用,否则被拦截的业务方法最终就无法被调用到。
l 如果想在被拦截的业务方法执行结束后在执行一些自定义的代码,可以在 proceed 执行后、拦截器方法返回之前加入自己的代码。
4) 因为拦截器方法和被拦截的业务方法处于相同的 java 调用堆栈,因此可以通过抛出异常,来终止方法的执行。
5) 除了在外部类中定义拦截器方法,还可以在会话 Bean 内部将 Bean 的一个或多个方法定义为拦截器。仍然是使用 @AroundInvoke 和上述的那种方法格式。执行流程也是一样的。
20. 使用拦截器
1) 使用外部类类型的拦截器,可以用 @Interceptors 注释。这个注释接受拦截器类的 .class 属性值,多个 .class 通过逗号分隔。可以标识在类级,表明拦截类中的所有方法;也可以标识在方法级,表明只对某一方法进行拦截。
2) 如果是在 Bean 类内部,将方法标识为 @AroundInvoke ,则该方法将会拦截 Bean 中的所有方法。
3) 默认拦截器
l 通过 ejb-jar.xml 可以定义默认拦截器, ejb-jar.xml 文件需要置于 jar 文件的 META-INF 目录:
<? xml version = "1.0" encoding = "UTF-8" ?>
< ejb-jar version = "3.0" xmlns = "http://java.sun.com/xml/ns/javaee"
xmlns:xml = "http://www.w3.org/XML/1998/namespace" xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation = "http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/ejb-jar_3_0.xsd " >
< assembly-descriptor >
< interceptor-binding >
< ejb-name > * </ ejb-name >
< interceptor-class > cn.gengv.ejb3ex.interceptor.LogInterceptor </ interceptor-class >
</ interceptor-binding >
</ assembly-descriptor >
</ ejb-jar >
l <ejb-name>*</ejb-name> 指明拦截所有的 EJB 的所有方法。如果只想拦截某一个 EJB ,则可以用这个 EJB 的名称替换通配符“ * ”。其效果和在 Bean 类上标识 @Interceptor 的效果 是一样的。
l <intercepter-class> 用来指定拦截器类。
4) 禁用拦截器
l 可以使用 @ExcludeDefaultInterceptors 注释来禁用默认拦截器,该注释可以标识在 Bean 类级或业务方法级。
l 虽然可以通过 @ExcludeDefaultInterceptors 注释来禁用默认拦截器, Bean 类仍然可以通过 @Interceptors 注释来使用自己所需要的拦截器。
5) 拦截生命周期事件
l 与定义 @AroundInvoke 方法非常类似,只是使用回调注释,替代 @AroundInvoke 。例如:
@PostConstruct public void methodName(InvocationContext ctx)
l 因为生命周期事件的回调方法没有返回值,因此拦截方法也必须是 void 。而且拦截方法不能有 throws 子句。
l 其他特性和 @AroundInvoke 拦截方法相同。
6) 根据自己的测试,默认拦截器会在 Bean 自己指定的拦截器之前调用