EJB3 CORE规范(第十二章拦截器)

来源:互联网 发布:网络平台战略合作协议 编辑:程序博客网 时间:2024/04/29 04:25
1             拦截器
拦截器是一个拦截业务方法调用和生命周期事件的方法。
1.1   概述
拦截器方法可以定义在企业bean的class上,也可以定义一个拦截器类,并在企业bean中引入。拦截器类(与bean的class不同)的方法用于响应对bean方法的调用和/或者生命周期事件。
一个bean上可以定义多个拦截器。
对一个单一业务方法的调用,InvocationContext对象的上下文数据可以穿越多个拦截器方法调用。
拦截器类必须有一个public的无参构造器。
对企业bean的组件的编程限制同样适用于拦截器。参看章节21.1.2
通过注释符或配置文件可以为bean定义拦截器方法和拦截器类。当使用注释符时,在bean本身和/或的业务方法上使用Interceptors来指定一到多个拦截器。如果定义多个拦截器,它们的执行顺序按照它们定义的顺序,参看12.3.1和12.4.1章节。配置文件用于覆盖和改变拦截器的执行顺序。
缺省的拦截器可以定义在ejb-jar文件级别上,这样,在jar文件的所有组件都会应用这个拦截器。参看12.6章节。
1.2   拦截器的生命周期
拦截器实例的生命周期和它关联的bean实例的生命周期一致。当创建bean时,会创建这个bean上所有拦截器类的实例。当bean实例被清除时,会销毁这些拦截器实例。如果拦截器和有状态会话bean关联,则当bean被钝化时会钝化拦截器实例,当bean实例被激活时会激活拦截器实例。参看4.4,4.5.1和5.5章节。
拦截器实例和bean实例在PostConstruct或PostActivate方法被调用之前被创建或被激活。在bean实例或拦截器实例被析构或钝化之前调用PreDestroy和PrePassivate方法。
拦截器实例可以保存状态。拦截器实例可以是依赖注入的目标。当创建拦截器实例时使用它关联的bean的命名上下文执行依赖注入。在拦截器实例和bean实例上的依赖注入都完成后调用PostConstruct方法。
拦截器可以调用JNDI,JDBC,JMS,其他的企业bean和EntityManager。参见表1,2,3.
Bean拦截器和被调用的bean方法与生命周期方法共享bean的命名上下文。用于依赖注入或直接JNDI查找的注释符和/或XML配置文件元素指向这个共享的命名上下文。
EJBContext对象可以注入到拦截器类。拦截器可以使用EJBContext的lookup方法来获取bean的JNDI命名上下文。
只支持在和有状态会话bean关联的拦截器上使用可扩展的持久化上下文。
1.3   业务方法拦截器
拦截器方法可以定义在会话bean的业务方法和消息bean的消息监听器方法上。业务方法拦截器通过AroundInvoke或around-invoke配置元素指定。
AroundInvoke方法可以定义bean的超类或拦截器类上。但是,在这些类上只能有一个AroundInvoke方法。AroundInvoke方法不能是bean的业务方法(即不能是bean的业务接口内定义的方法)。
AroundInvoke方法可以是public,private,protected,或包层级的方法。AroundInvoke方法不可以声明为final或static。
AroundInvoke方法有下面的标志符:
Object <METHOD>(InvocationContext) throws Exception
AroundInvoke方法可以调用任何业务无法方法可以调用的组件或资源。
业务方法拦截器方法可以只应用到单个的业务方法上,而不是bean的所有方法上。参见12.7章节“方法级的拦截器”。
1.3.1             多个业务方法拦截器方法
如果为bean定义了多个拦截器方法,则使用下面的规则来管理拦截器方法的调用顺序。配置文件可以覆盖在注释符内定义的拦截器顺序,参见12.8章节。
如果可能,首先调用缺省的拦截器方法。缺省的拦截器只能在配置文件中指定。按照在配置文件中指定的顺序执行缺省拦截器。
如果在bean的类上指定了拦截器类,则在执行bean本身的拦截器方法之前执行拦截器类上的拦截器方法。
按照在Interceptors注释符中的顺序执行在拦截器类内定义的AroundInvoke方法。
如果拦截器类有超类,则先执行它父类的拦截器方法。
在执行完拦截器类上拦截器方法后,那么按下述顺序执行:
如果业务方法上定义有方法级的拦截器类,那么按照在业务方法上Interceptors注释符指定的顺序执行拦截器类上的AroundInvoke方法。(参看12.7章节,方法级的拦截器描述)
如果bean有超类,那么执行超类上的AroundInvoke方法。
执行bean类本身的AroundInvoke方法。
如果AroundInvoke方法被另一个方法重载(不过这个方法是否是自己的AroundInvoke方法),则不会被调用。
配置文件可以用于覆盖在注释符内指定的拦截器调用顺序。参见12.8.2.
InvocationContext对象提供了可以使拦截器方法控制拦截器链的元数据,包括是否调用下一个链中的拦截器方法以及它的参数和返回的值。如何使用InvocationContext在12.5章节中描述。
1.3.2             异常
业务方法拦截器方法可以抛出运行时异常或业务方法throws子句内允许的应用异常。
AroundInvoke方法可以catch和忽略异常,并通过调用proceed()恢复。AroundInvoke方法可以抛出允许时异常或任何在业务方法throws子句内允许的可检查的异常。
AroundInvoke方法和bean的业务方法运行在同一个java堆栈中。AroundInvoke方法可以抛出允许时异常或任何在业务方法throws子句内允许的可检查的异常,除了引起java堆栈崩溃的异常。异常和初始化和/或清除操作通常应当位于proceed()的try/catch/finally块中。
AroundInvoke方法可以通过抛出运行时异常或通过调用EJBContext的setRollbackOnly()方法来标记事务回滚。AroundInvoke方法可以在InvocationContext.proceed()方法调用之前使事务回滚。
如果拦截器链中遗漏了未处理的系统异常,则bean实例和它相关的拦截器实例都会被销毁。在这种情况下不会调用PreDestroy方法:当拦截器链断裂时,在拦截器链中的拦截器方法应当执行必需的清除操作。
1.4   生命周期事件回调的拦截器
生命周期回调拦截器方法可以在会话bean和消息驱动bean上定义。
生命周期事件回调的拦截器可以定义在拦截器类和/或直接定义在bean的类上。PostConstruct,PreDestroy,PostActivate和PrePassivate注释符用于定义生命周期回调事件的拦截器方法。如果使用配置文件,则使用post-construct,pre-destroy,post-activate和pre-passivate元素。
生命周期回调拦截器方法和业务方法拦截器方法可以定义在同一个拦截器类上。
生命周期回调拦截器方法是否在事务和安全上下文中被调用,没有明确定义。
生命周期回调拦截器方法可以定义在bean的超类或拦截器的超类上。但是,这个类对同一个生命周期事件只能有一个生命周期回调拦截器方法。在类上可以指定部分或全部生命周期回调注释符。
单个生命周期回调拦截器方法可以用于多个回调事件(例如,PostConstruct和PostActivate)。
定义在拦截器类上的生命周期回调拦截器方法有下面的标志符:
void <METHOD> (InvocationContext)
定义在bean类上的生命周期回调拦截器方法有一下标志符:
void <METHOD>()
生命周期回调拦截器方法可以有public,private,protected,或包级可见符。生命周期回调拦截器方法不应当声明为final或static的。
例子:
@Stateful public class ShoppingCartBean implements ShoppingCart {
 private float total;
 private Vector productCodes;
 public int someShoppingMethod(){...};
 ...
 @PreDestroy void endShoppingCart() {...};
 }
public class MyInterceptor {
...
@PostConstruct
public void any-method-name (InvocationContext ctx) {
...
ctx.proceed();
...
}
@PreDestroy
public void any-other-method-name (InvocationContext ctx) {
...
ctx.proceed();
...
}
}
1.4.1             一个生命周期回调事件上有多个回调拦截器方法
如果一个生命周期回调事件上定义了多个回调拦截器方法,那么调用的顺序遵循下面的规则。配置文件可以覆盖在注释符内指定的调用顺序。参见12.8章节。
如果可能,首先调用缺省的拦截器方法。缺省的拦截器只能在配置文件中指定。按照在配置文件中指定的顺序执行缺省拦截器。
如果在bean的类上指定了拦截器类,则在执行bean本身的生命周期回调拦截器方法之前执行bean类上的拦截器类中定义的生命周期回调拦截器。
按照在Interceptors注释符中的顺序执行在生命周期回调拦截器类内定义的生命周期回调方法。
如果拦截器类有超类,则先执行父类的生命周期回调拦截器方法,然后在执行自身的生命周期回调拦截器方法。
在执行完拦截器类上拦截器方法后,那么按下述顺序执行:
如果bean有超类,那么执行超类上的生命周期回调拦截器方法。
执行bean类本身的生命周期回调拦截器方法。
如果生命周期回调方法被另一个方法重载(不过这个方法是否是自己的生命周期回调方法),则不会被调用。
配置文件用于覆盖在注释符中指定的拦截器调用顺序。参见12.8.2章节。
对给定生命周期事件的所有回调拦截器方法运行在同一个java堆栈中。如果在bean类(或它的超类)上没有对应的回调方法,那么在拦截器链中的最后一个拦截器方法法内执行InvocationContext.proceed()。
InvocationContext提供了拦截方法用于控制拦截器链下一个方法调用的元数据。参见12.5
1.4.2             异常
生命周期回调拦截器方法可以抛出系统异常,不是应用异常。
由生命周期回调拦截器方法抛出的运行时异常在拦截器链断裂后会引起bean实例和拦截器实例被销毁。
生命周期回调拦截器方法和bean上的生命周期回调方法运行在同一个java堆栈中。InvocationContext.proceed()抛出和另外一个生命周期回调拦截器方法或bean类上的生命周期回调方法相同的异常,除非拦截器使得java堆栈崩溃而抛出其他的异常。生命周期回调拦截器方法(而不是bean上或它超类上的方法)可以catch由另外一个生命周期回调拦截器方法抛出的异常,并且在返回之前清除它。通常情况下,应在在proceed()方法的try/catch/finally块中处理异常和初始化和/或清除操作。
当bean和拦截器由于这些异常被销毁时,不会调用PreDestroy方法:在拦截器链中的生命周期回调拦截器方法应该在拦截器链断裂时执行必需的清除操作。
1.5   InvocationContext
InvocationContext对象提供了可以使拦截器方法控制调用链行为的元数据。
public interface InvocationContext {
public Object getTarget();
public Method getMethod();
public Object[] getParameters();
public void setParameters(Object[] params);
public java.util.Map<String, Object> getContextData();
public Object proceed() throws Exception;
}
同一个InvocationContext实例可以传递到业务方法或生命周期事件的每一个拦截器方法中。这样,拦截器就可以将信息保存到InvocationContext的上下文数据属性中,然后可以被后续的拦截器获取,这样就可以做到在多个拦截器之间传递数据。这些数据不会在多个业务方法调用或生命周期回调事件间共享。如果拦截器作为对web服务终端调用的结果,那么由getContextData返回的map将是JAX-WS MessageContext。因此,InvocationContext实例的生命周期没有明确指定。
getTarget方法返回bean的实例。getMethod返回拦截器将要调用的方法。对应AroundInvoke方法,那就是bean的业务方法;对于生命周期回调拦截器方法,getMethod返回null。
proceed方法会调用拦截器链中的下一个拦截器,如果是最后一个拦截器,则会调用业务方法。拦截器方法必须总是调用InvocationContext.proceed()或者不调用后续的拦截器方法、bean的业务方法或生命周期回调方法。Proceed方法返回先一个方法调用的结果。如果方法返回void,那么proceed返回null。对于生命周期回调拦截器方法,如果在bean类上没有定义回调方法,那么必须在最后一个拦截器方法内调用proceed方法,并且返回null。如果有多个拦截器方法,则proceed方法会让容器按照顺序调用它们的方法。
1.6   缺省拦截器
缺省拦截器应用于ejb-jar包内的所有组件上。配置文件用于定义缺省拦截器以及它们的顺序。参见12.8.2。
缺省拦截器自动应用到ejb-jar内的所有组件上。ExcludeDefaultInterceptors注释符或exclude-default-interceptors配置符用于排除缺省拦截器的调用。
在bean的拦截器被调用之前调用缺省拦截器。Interceptor-order配置描述元素用于指定可选的顺序。参见12.8.2章节。
1.7   方法级的拦截器
业务方法上可以定义业务方法拦截器方法,但不是所有的业务方法(注:方法级的拦截器用于指定业务方法拦截器方法。如果用作方法级拦截器的拦截器类顶一个了生命周期回调拦截器方法,那么这些生命周期回调拦截器方法不会被调用。参见12.4.1)。
通过在方法上使用Interceptors注释符或者通过使用interceptor-bingding配置描述元素,可以定义特定的业务方法拦截器。如果一个业务方法上定义了多个方法级的拦截器,那么按照指定的顺序调用拦截器。按照在章节12.3.1中的描述调用方法级的业务方法拦截器以及缺省拦截器和定义在bean类(以及它的超类)上的拦截器。配置描述文件可以用于覆盖调用顺序。
同一个拦截器可以应用到bean类上的多个方法上:
@Stateless
public class MyBean ... {
public void notIntercepted() {}
@Interceptors(org.acme.MyInterceptor.class)
public void someMethod() {
}
@Interceptors(org.acme.MyInterceptor.class)
public void anotherMethod() {
}
}
在一个业务方法上使用多个方法级的拦截器不影响拦截器实例和bean类之间的关系——对每个拦截器类,每个bean实例只会创建一个拦截器实例。
当ExcludeDefaultInterceptors注释符或exclude-default-interceptors配置描述元素应用到业务方法时,用于排除缺省的拦截器调用。相似地,ExcludeClassInterceptors注释符或exclude-class-interceptors配置描述元素用于排除类级别的拦截器调用(注:类级别的拦截器指由Interceptors注释定义在bean类上的拦截器(或者由配置文件指定))。
在下面的例子中,如果没有缺省的拦截器,当调用someMethod时,只会调用MyInterceptor。
@Stateless
@Interceptors(org.acme.AnotherInterceptor.class)
public class MyBean ... {
...
@Interceptors(org.acme.MyInterceptor.class)
@ExcludeClassInterceptors
public void someMethod() {
}
}
如果bean类上也定义了缺省拦截器,可以通过使用ExcludeDefaultInterceptors注释符在特定的方法上排除它。
@Stateless
@Interceptors(org.acme.AnotherInterceptor.class)
public class MyBean ... {
...
@ExcludeDefaultInterceptors
@ExcludeClassInterceptors
@Interceptors(org.acme.MyInterceptor.class)
public void someMethod() {
}
}
1.8   配置文件中的拦截器规范
配置文件是注释符的另外一个指定拦截器和EJB的方式,也可以用于覆盖在注释符中指定的拦截器的顺序。
1.8.1             拦截器规范
配置元素interceptor用于指定拦截器类的拦截器方法。拦截器方法用around-invoke,pre-contsturct,pre-destroy,pre-passivate和post-activate元素来指定。
不管是单独使用配置文件定义拦截器,还是注释符和配置文件一块来定义拦截器,拦截器类最多能有一个around-invoke方法,pre-construct方法,post-destroy方法,per-passivate方法或post-activate方法。
1.8.2             拦截器绑定到bean的规范
元素interceptor-binding用于指定拦截器类与企业bean和它们的业务方法的绑定。下面是interceptor-binding子元素:
元素ejb-name必须是企业bean的名字或者是通配符“*”(它用于定义那些绑定到ejb-jar包内的所有bean的拦截器)
元素interceptor-calss指定拦截器类。元素interceptor-order是为给定的层级及其更高层级指定拦截器总顺序的可选的替代方法。
元素method-name指定方法级拦截器方法名称;可选的method-params元素用于在多个同名方法中定位一个方法。
用通配符“*”绑定到所有类的拦截器都是缺省拦截器。另外,拦截器可以被绑定到bean的class(类级别的拦截器)或类的业务方法(方法级别的拦截器)上。
拦截器到类的绑定是附加的。如果拦截器被绑定到类级别或/和缺省级别和方法级别,那么将使用类级别和/或缺省级别和方法级别。配置文件可以用于增强通过注释符定义的拦截器和拦截器方法。当配置文件用于增强在注释符内指定的拦截器时,将会根据在12.3.1和12.4.1章节中指定的顺序调用配置文件内的拦截器方法。元素interceptor-order也可以覆盖在注释符内定义的顺序。
元素exclude-default-interceptors排除掉指定层级和它以下层级的缺省拦截器。也就是说,当在类层级上使用exclude-default-interceptors时,将会排除掉这个类上所有方法的缺省拦截器。当在方法层级上使用exclude-default-interceptors时,只会排除掉这个方法上的缺省拦截器。显式地在较低层级上列出需排除的上层拦截器将不会在这个层级及其一下层级上调用这些列出的拦截器。
通过使用interceptor-order元素指定类层级和/或方法层级上的拦截器顺序,可以覆盖在章节12.3.1和12.4.1中指定的拦截器顺序。如果使用interceptor-order元素,那么在给定层级上指定的顺序必须是所有在这个层级及其以上层级上的拦截器的顺序(除非那些用上面描述的exclude-元素显式地被排除的拦截器)。(注:如果拦截器顺序不完备,则配置员必须保证满足这些条件)。
拦截器元素语法有四种可能的风格:
风格1:
<interceptor-binding>
<ejb-name>*</ejb-name>
<interceptor-class>INTERCEPTOR</interceptor-class>
</interceptor-binding>
用通配符“*”指定ejb-name元素来指派缺省拦截器(这些拦截器应用到ejb-jar包内的所有企业bean)。
风格2:
<interceptor-binding>
<ejb-name>EJBNAME</ejb-name>
<interceptor-class>INTERCEPTOR</interceptor-class>
</interceptor-binding>
这个风格用于指定与特定企业bean类相关的拦截器(类级别拦截器)。
风格3:
<interceptor-binding>
<ejb-name>EJBNAME</ejb-name>
<interceptor-class>INTERCEPTOR</interceptor-class>
<method-name>METHOD</method-name>
</interceptor-binding>
这个风格用于为特定bean的特定方法指定关联的方法级的拦截器。如果多个方法具有相同的名字,那么这个风格的元素指具有相同名字的所有方法。方法级的拦截器只能和bean的业务方法相关联。注意:在方法级的拦截器不能使用通配符“*”。
风格4:
<interceptor-binding>
<ejb-name>EJBNAME</ejb-name>
<interceptor-class>INTERCEPTOR</interceptor-class>
<method-name>METHOD</method-name>
<method-params>
<method-param>PARAM-1</method-param>
<method-param>PARAM-2</method-param>
...
<method-param>PARAM-n</method-param>
</method-params>
<interceptor-binding>
这个风格用于指定与特定企业bean的特定方法关联的方法级拦截器。这个风格用于在许多重名方法中指定其中的一个方法。PARAM-1到PARAM-n的值事方法入参的java类型的全称(如果方法没有入参,那么method-params不包含method-param元素)。数组是通过数组元素类型来指定,元素后加上一到多个“[]”(例如,int[][])。
如果对同一个企业bean同时使用风格3和风格4来定义方法级的拦截器,那么这些方法级拦截器的顺序没有明确规定。
1.8.2.1 例子
下面是使用interceptor-binding语法的例子。
风格1:下面的拦截器作为缺省拦截器应用到ejb-jar中的所有组件上。他们将按照指定的顺序被调用。
<interceptor-binding>
<ejb-name>*</ejb-name>
<interceptor-class>org.acme.MyDefaultIC</interceptor-class>
<interceptor-class>org.acme.MyDefaultIC2</interceptor-class>
</interceptor-binding>
风格2:下面的例子是定义EmployeeService企业bean的方法级拦截器。它们会在缺省拦截器被调用后按照指定的顺序被调用。
<interceptor-binding>
<ejb-name>EmployeeService</ejb-name>
<interceptor-class>org.acme.MyIC</interceptor-class>
<interceptor-class>org.acme.MyIC2</interceptor-class>
</interceptor-binding>
风格3:下面的拦截器应用到EmployeeService企业bean的所有myMethod方法上。他们会在所有缺省的拦截器和类层级的拦截器被调用后按照指定的顺序被调用。
<interceptor-binding>
<ejb-name>EmployeeService</ejb-name>
<interceptor-class>org.acme.MyIC</interceptor-class>
<interceptor-class>org.acme.MyIC2</interceptor-class>
<method-name>myMethod</method-name>
</interceptor-binding>
风格4:下面的拦截器应用到EmployeeService企业bean的myMethod(String firstName, String lastName)方法指定拦截器。
<interceptor-binding>
<ejb-name>EmployeeService</ejb-name>
<interceptor-class>org.acme.MyIC</interceptor-class>
<method-name>myMethod</method-name>
<method-params>
<method-param>java.lang.String</method-param>
<method-param>java.lang.String</method-param>
</method-params>
</interceptor-binding>
下面的例子用更复杂的参数类型来解释风格3.方法myMethod(char s, int I, int[] iar, mypackage.MyClass mycl, mypackage.MyClass[][] myclaar)指定如下:
<interceptor-binding>
<ejb-name>EmployeeService</ejb-name>
<interceptor-class>org.acme.MyIC</interceptor-class>
<method-name>myMethod</method-name>
<method-params>
<method-param>char</method-param>
<method-param>int</method-param>
<method-param>int[]</method-param>
<method-param>mypackage.MyClass</method-param>
<method-param>mypackage.MyClass[][]</method-param>
</method-params>
</interceptor-binding>
下面的例子解释用interceptor-order元素来指定拦截器的顺序:
<interceptor-binding>
    <ejb-name>EmployeeService</ejb-name>
<interceptor-order>
<interceptor-class>org.acme.MyIC</interceptor-class>
<interceptor-class>org.acme.MyDefaultIC</interceptor-class>
<interceptor-class>org.acme.MyDefaultIC2</interceptor-class>
<interceptor-class>org.acme.MyIC2</interceptor-class>
</interceptor-order>
</interceptor-binding>
原创粉丝点击