您的位置:首页 > 运维架构

EJB中的AOP:拦截器

2016-08-21 22:40 225 查看
AOP是什么

        AOP是一种面向切面编程的思想,AOP背后的本质概念是,对于大多数应用程序来说,都有一些共同的代码,它们在许多方法中重复出现,与解决核心业务问题并不一定直接相关,只是用来处理各种基础设施问题。

        最常被引用的例子是日志记录,尤其是在基础调试级别。我们假设要在进入系统中每个方法时记入日志。如果没有AOP,这就表示要在系统的每个方法的开始添加日志记录语句,把“进入方法xx”的操作记录到日志!适用AOP的一些其他常见的例子还包括审核、性能分析和统计。

        描述这种情况的常用术语是横切关注点(crosscutting concern)——跨越整个应用程序逻辑 的关注点。AOP系统允许把横切关注点分别放到单独的模块中,然后将这些模块应用于相关的应用程序代码中,比如每个方法调用的开头。

拦截器是什么

        拦截器就是EJB版的AOP。它是调用EJB方法时将自动触发的对象。EJB 3拦截器在方法的开头和方法返回时被触发,它们可以检查方法的返回值或方法抛出的任何异常。拦截器可以应用于会话bean和消息驱动bean。



       业务拦截器通常用于实现通用代码。ActionBazaarLogger实现ActionBazaar系统中所有EJB都会使用的通用日志记录代码。

EJB业务方法拦截器关键代码如下:

@Stateless
public class PlaceBidBean implements PlaceBid {
···
//附加拦截器
@Interceptors(Interceptor.class)
public Long addBid(String userId, Long itemId, Double bidPrice) {
	···
}
}
 
public class Interceptor{
//指定拦截器方法
@AroundInvoke
public Object logMethodEntry(InvocationContext invocationContext)
throws Exception{
System.out.pritln("Entering method:"+invocationContext.getMethod().getName());
return invocationContext.proceed();
}
}


指定拦截器

       @Interceptors注解允许为方法或类指定一个或多个拦截器类,@Interceptors注解也可以应用于整个类,这样如果调用目标类的任一方法,就会触发拦截器。@Interceptors注解能够在类或方法级别附加一个以上的拦截器,具体的方法是在注解中加上以逗号分隔的参数列表,如下所示:

@Interceptors({Interceptor.class,One.class,...})


        除指定方法和类级别的拦截器之外,还可以创建默认拦截器(default interceptor)。默认拦截器在本质上是附加到模块中的每个bean方法的包含各种功能的机制,但是不能使用注解指定这种类型的拦截器,只能使用部署描述文件进行设置。代码如下:

<assembly-descriptor>
<interceptor-binding>
<ejb-name>*</ejb-name> 
<interceptor-class> 
	actionbazaar.buslogic.
Interceptor
</interceptor-class> 
</interceptor-binding>
</assembly-descriptor>


       如果对特定目标方法指定了默认的、类级别的和方法级别的拦截器,他们怎么排序呢?与Java作用域管理通常的工作方式不同,拦截器的调用是从大的到小作用域进行的。也就是说,首先触发默认拦截器,然后触发类级别拦截器,最后是方法级别拦截器。



        改变这种执行顺序的唯一方法是使用部署描述文件中的iterceptor-order元素,没奋用于改变拦截器顺序的注解。你也可以禁止默认级别或类级别的拦截器。对类或方法应用@javax.interceptor.ExcludeDefaultInterceptors注解可以禁止类或方法的所有默认拦截器。类似地,@javax.interceptor.ExcludeClassInterceptors注解会禁止方法的类级别拦截器。

实现业务拦截器

1.环绕调用通知

       拦截器永远只有种方法被指定为环绕调用方法(around invoke[AroundInvoke]method)。环绕调用方法不能是业务方法,也就是说它们不应该是bean的业务接口中的公有方法。当客户端调用拦截器指定的方法时,容器自动触发环绕调用方法。被指定为AroundInvoke的任何方法都必须遵守这种模式:

Object <METHOD>(InvocationContext)

       对拦截器的功能而言,调用proceed方法极为关键。我们通过InvocationContext.proceed返回对象,这样通知容器应该处理执行链中的下一个拦截器或调用被拦截的业务方法。如果不调用proceed方法,就会使处理暂停并且不会调用业务方法(以及执行链下面的任何其他拦截器)

2.InvocationContext

InvocationContext接口还有很多其他有用的方法。下面是此接口的定义:

public iterface InvocationContext{
public Object getTarget();
public Method getMethod();
public Object[] getParameters();
public void setParameters();
public java.util.Map<String,Object> getContextData();
public Object proceed() throws Exception;
}


getTarget方法获得被拦截方法所属的bean实例。当通过其实例变量或访问器方法检查bean的当前状态时,这个方法特别有用。

getMethod方法返回调用拦截器的bean类的方法。对于AroundInvoke方法而言,它退回bean类上的业务方法;对于生命周期回调拦截器方法而言,getMethod返回null。

getParameters方法返回传递给被拦截方法的参数,返回形式为对象数组。setParameters方法允许我们在运行时,在将参数值传递给方法之前修改这些值。这两个方法帮助我们通过维护bean参数的拦截器修改运行时行为。

InvocationContext.getContextData方法重要性的关键在于给定方法跨越拦截器链共享上下文。这样,附加到InvocationContext的数据可以用于拦截器之间的通信。假设我们的安全认证拦截器在通过用户认证后,把成员状态存储在调用上下文数据中:

invocationContext.getContextData().put("MemberStatus",”Gold”);


       上下文数据调用是简单的Map,用于存储名称-值对,现在,调用链中的另一个拦截器可以检索此数据并且根据成员状态采取特定操作。检索成员状态的代码如下所示:

String memberStatus =(String)invocationContext.getContextData().get("MemberStatus");


       你可以在业务方法拦截器中抛出或处理运行时或可控异常。如果业务方法拦截器在调用proceed方法之前抛出异常,则会终止调用链中的其他拦截器和目标业务方法的处理。

拦截器类中的回调方法

       生命周期回调也是拦截器的一种形式,当调用业务方法时触发方法拦截器,当bean从一种生命周期状态转换到另一种时触发生命周期回调。这样的方法可以用于处理跨越bean共事的横切事项。除业务方法拦截器之外,你还可以在拦截器类中定义生命周期回调。生命周期回调的中所有注解,如@PostActivate等,应用与拦截器方法时,生命周期回调的工作方式完全相同。在拦截器类中定义的生命周期回调也被称为生命周期回调拦截器(lifecycle
callback interceptor) 或生命周期回调监听器 (lifecycle callback listener)。当目标bean转换生命周期状态时,就会触发拦截器类中加了注解的方法。当构造或销毁bean实例时,下面的拦截器类将会把ActionBazaar的bean何时分配和释放资源的情况记录到日志。

public class ActionBazaarResourceLogger{
@PostConstruct
public void innitialize(InvocationContext context){
System.out.println("Allocating resources for bean:"+context.getTarget());
context.proceed();
}
 
@PreDestroy
public void cleanup(InvocationContext context){
System.out.println("Releasing resources for bean:"+context.getTarget());
context.proceed();
}	
}


        生命周期拦截器方法不能抛出可控异常(因为生命周期回调没有对应的客户端去报告问题, 所以这样做没有意义)。

注意,bean可以在自身中和一个或多个拦截器中具有相同的生命周期回调。这就是在生命周期拦截器方法中调用InvocationContext.proceed调用方法的重点所在,就像在资源日志记录器中一样。它确保调用链中的下一个生命周期拦截器或bean生命周期方法会被触发。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  EJB