CDI services--interceptors(拦截器)

摘要:
publicclassTransactionInterceptor{@AroundInvokepublicObjectmanageTransaction(InvocationContextx)throws Exception{…}}@ArundInvoke注释指定要用作拦截器的方法。
1.拦截器综述

拦截器的功能是定义在Java拦截器规范。

拦截器规范定义了三种拦截点: 

  1. 业务方法拦截,
  2. 生命周期回调侦听,
  3. 超时拦截(EJB)方法。

在容器的生命周期中进行拦截 

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

EJB超时时使用的拦截器 

public class TimeoutInterceptor {

   @AroundTimeout 
   public Object manageTransaction(InvocationContext ctx) throws Exception { ... }
}

在业务上,对某一个Bean的方法进行拦截 

public class TransactionInterceptor {

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

@AroundInvoke注释指定了要用作拦截器的方法,拦截器方法与被拦截的业务方法执行同一个java调用堆栈、同一个事务和安全上下文中。用@AroundInvoke注释指定的方法必须遵守以下格式:public Object XXX(javax.interceptor.InvocationContext ctx) throws Exception 

下面是javax.interceptor.InvocationContext封装了客户端所调用业务方法的一些信息。 

package javax.interceptor;
public interface InvocationContext{
    public Object getTarget();
    public Method getMethod();
    public Ojbect[] getParameters();
    public void setParameters(Object[] newArgs);
    public java.util.Map<String, Ojbect> getContextData();
    public Object proceed() throws Exception;
}
  • getTarget() 指向被调用的bean实例
  • getMethod() 指向被拦截的业务方法
  • getParameters() 获取被拦截业务方法的参数
  • setParameters() 设置被拦截业务方法的参数
  • getContextData() 返回一个Map对象,它在整个方法调用期间都可以被访问到。位于同一个方法调用内的不同拦截器之间可以利用它来传递上下文相关的数据。

示例: 

//被拦截的方法
@Interceptors(HelloInterceptor.class) 
public class HelloChinaBean {
    public String SayHello(String name) {
        return name +"Hello World.";
    }
}
//拦截器定义
public class HelloInterceptor {
    @AroundInvoke
    public Object log(InvocationContext ctx) throws Exception {
       
        try{
            if (ctx.getMethod().getName().equals("SayHello")){
                System.out.println("Holle World!!!" );
            }            
            return ctx.proceed();
        }catch (Exception e) {
            throw e;
        } 
    }
}
2.拦截器绑定(Interceptor bindings)

假设我们想要申明一些bean的事务。我们先要的是一个拦截器绑定类型来指定哪些bean我们要申明. 
首先定义一个注解 

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

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

@Transactional
public class ShoppingCart { ... }

或者我们可以指定一个方法的事务 

public class ShoppingCart {
   @Transactional public void checkout() { ... }
}
2.拦截器实现(Implementing interceptors)

我们实际上要实现提供了这种事务管理方面的拦截器,所以我们需要做的是创建一个标准的拦截,并配上@Interceptor和@transactional注解. 

@Transactional @Interceptor
public class TransactionInterceptor {

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

拦截器可以利用依赖注入: 

@Transactional @Interceptor
public class TransactionInterceptor {

    @Resource UserTransaction transaction;

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

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

@Resource和@Inject的区别:

CDI services--interceptors(拦截器)第1张

3.启用拦截器(Enabling interceptors)

默认情况下,所有拦截器被禁用.要使用拦截器.需要在bean.xml中进行配置,以启用.从CDI 1.1起拦截器可以使用@Priority注释为整个应用程序启用。 

<beans
   xmlns="http://xmlns.jcp.org/xml/ns/javaee"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="
      http://xmlns.jcp.org/xml/ns/javaee
      http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd">
   <interceptors>
      <class>org.mycompany.myapp.TransactionInterceptor</class>
   </interceptors>
</beans>

这样有2个好处:

  • 拦截器比较重要,在XML中确保其确定性行为
  • 它让我们在部署时启用或禁用拦截器类。

当然也可以配置启用多个拦截器

<beans
   xmlns="http://xmlns.jcp.org/xml/ns/javaee"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="
      http://xmlns.jcp.org/xml/ns/javaee
      http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd">
   <interceptors>
      <class>org.mycompany.myapp.SecurityInterceptor</class>
      <class>org.mycompany.myapp.TransactionInterceptor</class>
   </interceptors>
</beans>

拦截器毕竟比较重要,不推荐使用@Priority启用.
在CDI中,XML配置的优先级高于@Priority.
关于@Priority可以参考下列:

public static class Interceptor.Priority
extends Object
Priorities that define the order in which interceptors are invoked. These values should be used with the Priority annotation.

Interceptors defined by platform specifications should have priority values in the range PLATFORM_BEFORE up until LIBRARY_BEFORE, or starting at PLATFORM_AFTER.
Interceptors defined by extension libraries should have priority values in the range LIBRARY_BEFORE up until APPLICATION, or LIBRARY_AFTER up until PLATFORM_AFTER.
Interceptors defined by applications should have priority values in the range APPLICATION up until LIBRARY_AFTER.
An interceptor that must be invoked before or after another defined interceptor can choose any appropriate value.

Interceptors with smaller priority values are called first. If more than one interceptor has the same priority, the relative order of these interceptor is undefined.

For example, an extension library might define an interceptor like this:

@Priority(Interceptor.Priority.LIBRARY_BEFORE+10)
 @Interceptor
 public class ValidationInterceptor { ... }
4.Interceptor bindings with members(拦截器注解属性)

假设我们想要添加一些额外的信息给我们的@transactional注解: 

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

   boolean requiresNew() default false;
}
CDI将使用requiresNew的值选择两个不同的拦截器,TransactionInterceptor和RequiresNewTransactionInterceptor 


下面是requiresNew为true的拦截器

@Transactional(requiresNew = true) @Interceptor
public class RequiresNewTransactionInterceptor {

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

如下使用:

@Transactional(requiresNew = true)
public class ShoppingCart { ... }

但是如果我们只有一个拦截器,我们希望容器拦截器绑定时忽略requiresNew的值,也许这些信息只用于拦截器实现。我们可以使用@Nonbinding注释: 

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

   @Nonbinding String[] rolesAllowed() default {};
}
5.Multiple interceptor binding annotations(多重拦截器绑定注解)

通常我们使用拦截器绑定的组合类型绑定多个拦截器bean。例如,下面的声明将用于绑定TransactionInterceptor和SecurityInterceptor这2个拦截器到ShoppingCart.

@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() { ... }
}
@Transactional
public class ShoppingCart {

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

   public void checkout() { ... }
}
6. Interceptor binding type inheritance(拦截器绑定类型继承)

Java语言支持注解的一个限制就是缺乏注解继承.注解应该重用内置已有的.就如同下面这段代码表达的意思

//实际没这写法
public @interface Action extends Transactional, Secure { ... }

幸运的是,CDI围绕Java没有的这个特性开展了一些工作. 
我们会标注一个拦截器绑定类型,其有其他拦截器的绑定类型,(称为元注解) 
表述起来有点费劲,就如同下面代码这样.

@Transactional @Secure
@InterceptorBinding
@Target(TYPE)
@Retention(RUNTIME)
public @interface Action { ... }

现在任何Bean绑定 Action这个注解 ,其实就是绑定到了@Transactional @Secure.(就是拦截器TransactionInterceptor和拦截器SecurityInterceptor). (甚至TransactionalSecureInterceptor,如果它存在.)

7.Use of @Interceptors(同时用多个拦截器)

这个注解@Interceptors是拦截器规范定义的,cdi是支持的<使用托管bean和EJB规范>.如下:

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

   public void checkout() { ... }
}

但缺点也很明显,不推荐使用.缺点如下:

  1. 拦截器在代码中是硬编码.
  2. 拦截器在部署时不好更改.
  3. 拦截器命令是非全局的——它是在类级别由拦截器的顺序列出.

因此还是使用上面CDI的使用方式比较好.

免责声明:文章转载自《CDI services--interceptors(拦截器)》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇移动端界面适配hadoop yarn ui applications list 研究下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

JAVA_SE基础——39.继承

在面向对象程序设计中,可以从已有的类派生出新类。 这称做继承(inheritance)。 白话解释: 例子1:继承一般是指晚辈从父辈那里继承财产,也可以说是子女拥有父母给予他们的东西。    例子2:猫和狗都属于动物,程序中便可以描述为猫和狗继承自动物。同理,咖啡猫和波斯猫继承自猫,而泰迪犬和比特犬继承自狗。所有的动物之间都会形成一个继承体系,具体下...

URL 路由简介

URL 路由简介 在本教程中,将介绍对于每个 ASP.NET MVC 应用程序都非常重要的 URL 路由功能。URL 路由模型负责将进入的浏览器请求映射到特定的 MVC 控制器操作。 在本教程的第一部分中,我们将学习标准路由表如何将请求映射到控制器操作。在本教程的第二部分中,我们将学习如何使用自定义路由修改默认的路由表。 使用默认路由表 在创建新的 ASP...

JVM知识整理和学习(转载并修改)

  JVM是虚拟机,也是一种规范,他遵循着冯·诺依曼体系结构的设计原理。   冯·诺依曼体系结构中,指出计算机处理的数据和指令都是二进制数,采用存储程序方式不加区分的存储在同一个存储器里,并且顺序执行,指令由操作码和地址码组成,操作码决定了操作类型和所操作的数的数字类型,地址码则指出地址码和操作数。   从dos到window8,从unix到ubuntu和...

微信小程序最新获取用户头像昵称的方法

官方提供的最新方法Open-data标签,使用这个标签可以不用用户授权直接获得头像和昵称 微信小程序获取用户信息的两种方法wx.getUserInfo&open-data https://blog.csdn.net/lucky_Zeng/article/details/80066479...

Directory Opus(DO) 个人使用经验 2.0

设置已有命令的快捷键 设置方法 保存显示格式 保存方法 取消删除确认框 Windows取消删除确认框DO取消删除确认框 设置默认布局 设置方法 备份与恢复 设置已有命令的快捷键 已有命令指的是菜单栏上我们可以进行点击的所有操作,如下图所示。 设置方法 右上角 帮助-快捷键 打开快捷键设置窗口 此时右键任意命令,选择编辑,即可弹出菜单给该命令设...

C#中析构函数,命名空间及字符串的运用(Ninth day)

又到了总结知识的时间了,今天在云和学院学习了析构函数,命名空间及字符串的处理,现在就为大家总结下来。 理论: 析构函数 不能在结构中定义析构函数。只能对类使用析构函数。 一个类只能有一个析构函数。 无法继承或重载析构函数。 无法调用析构函数。它们是被自动调用的。 析构函数既没有修饰符,也没有参数。 命名空间 •namespace(命名空间),用于解决类重...