SeamFramework.orgCommunity Documentation

第 7 章 拦截器

7.1. 拦截器绑定
7.2. 实现拦截器
7.3. 激活拦截器
7.4. 成员拦截器绑定
7.5. 多个拦截器绑定注释
7.6. 拦截器绑定类型的继承
7.7. @Interceptors 的使用

Web Beans重用了EJB3.0的基本的拦截器体系,并且在两个方向上扩展了其功能:

EJB规范定义了两种拦截点:

一个 业务方法拦截 是Web Bean的客户应用在Web Bean的方法调用上:

public class TransactionInterceptor {

    @AroundInvoke public Object manageTransaction(InvocationContext ctx) { ... }
}

一个 生命周期回调拦截器 是容器应用在生命周期回调方法的调用上:

public class DependencyInjectionInterceptor {

    @PostConstruct public void injectDependencies(InvocationContext ctx) { ... }
}

一个拦截器类既可以拦截生命周期回调方法,也可以拦截业务方法。

假定我们想声明我们的某些Web Beans是事务性的。我们要做的第一个事情就是需要一个 拦截器绑定注释 以便指定哪些Web Beans是事务性的:

@InterceptorBindingType

@Target({METHOD, TYPE})
@Retention(RUNTIME)
public @interface Transactional {}

现在我们可以很容易地指定我们的 ShoppingCart 是一个事务性的对象:

@Transactional

public class ShoppingCart { ... }

或者,如果我们愿意的话,我们可以仅仅指定一个方法是事务性的:

public class ShoppingCart {

    @Transactional public void checkout() { ... }
}

很好,但是在代码中有些地方需要我们实现提供事务管理的拦截器。我们需要做的事情就是创建一个标准的EJB拦截器,使用 @Interceptor@Transactional注释它。

@Transactional @Interceptor

public class TransactionInterceptor {
    @AroundInvoke public Object manageTransaction(InvocationContext ctx) { ... }
}

所有的Web Beans拦截器都是简单的Web Beans,因此可以使用依赖注入和具有上下文的生命周期的管理。

@ApplicationScoped @Transactional @Interceptor

public class TransactionInterceptor {
    @Resource Transaction transaction;
    @AroundInvoke public Object manageTransaction(InvocationContext ctx) { ... }
    
}

多个拦截器可以使用相同的拦截器绑定类型

最后,我们需要在 web-beans.xml 配置文件中 激活 我们的拦截器。


<Interceptors>
    <tx:TransactionInterceptor/>
</Interceptors
>

哇塞!为何使用尖括号?

好吧,XML声明解决了两个问题:

例如,我们可以指定在 TransactionInterceptor 之前运行我们的安全拦截器。


<Interceptors>
    <sx:SecurityInterceptor/>
    <tx:TransactionInterceptor/>
</Interceptors
>

或者我们可以在测试环境中将它们都关闭!

假定我们想要往 @Transactional 注释中添加一些额外信息:

@InterceptorBindingType

@Target({METHOD, TYPE})
@Retention(RUNTIME)
public @interface Transactional {
    boolean requiresNew() default false;
}

Web Beans将使用 requiresNew 的值来决定在两种不同的拦截器 TransactionInterceptorRequiresNewTransactionInterceptor 中选择哪一个。

@Transactional(requiresNew=true) @Interceptor

public class RequiresNewTransactionInterceptor {
    @AroundInvoke public Object manageTransaction(InvocationContext ctx) { ... }
}

现在我们可以像这样使用 RequiresNewTransactionInterceptor

@Transactional(requiresNew=true)

public class ShoppingCart { ... }

但是如果我们只有一个拦截器,并且我们希望管理器在绑定拦截器的时候忽略 requiresNew 的值,那该怎么办?我们可以使用 @NonBinding 注释:

@InterceptorBindingType

@Target({METHOD, TYPE})
@Retention(RUNTIME)
public @interface Secure {
    @NonBinding String[] rolesAllowed() default {};
}

通常我们可以组合拦截器绑定类型将多个拦截器绑定到一个Web Bean上。例如,下面的声明可以将 TransactionInterceptorSecurityInterceptor 绑定到相同的Web Bean上:

@Secure(rolesAllowed="admin") @Transactional

public class ShoppingCart { ... }

然而,在非常复杂的情况下,一个拦截器本身可以指定一些拦截器绑定类型的组合:

@Transactional @Secure @Interceptor

public class TransactionalSecureInterceptor { ... }

然后,这个拦截器可以使用下面的任何一个组合来绑定到 checkout() 方法上:

public class ShoppingCart {

    @Transactional @Secure public void checkout() { ... }
}
@Secure

public class ShoppingCart {
    @Transactional public void checkout() { ... }
}
@Transactionl

public class ShoppingCart {
    @Secure public void checkout() { ... }
}
@Transactional @Secure

public class ShoppingCart {
    public void checkout() { ... }
}

Java语言对注释的支持有一个限制,那就是缺少注释的继承机制。实际上,注释应该可以重用内置,应该支持下列工作:

public @interface Action extends Transactional, Secure { ... }

不过幸运的是,Web Beans能够解决这个Java缺失的特性。我们可以向一个拦截器绑定类型注释另一个拦截器绑定类型。拦截器绑定是具有传递性的 — 拥有第一个拦截器绑定的任何Web Bean都继承被声明为元注释的拦截器绑定。

@Transactional @Secure

@InterceptorBindingType
@Target(TYPE)
@Retention(RUNTIME)
public @interface Action { ... }

任何使用 @Action 注释的Web Bean都被绑定到 TransactionInterceptorSecurityInterceptor 。(如果存在 TransactionalSecureInterceptor 的话,甚至可以绑定到这个拦截器)

企业级和简单的Web Bean都支持EJB规范指定的 @Interceptors 注释,例如:

@Interceptors({TransactionInterceptor.class, SecurityInterceptor.class})

public class ShoppingCart {
    public void checkout() { ... }
}

然而,这种方法具有下列缺陷:

因此,我们建议使用Web Bean风格的拦截器绑定。