一、Java平台上AOP实现机制
1、jdk动态代理:利用拦截器(拦截器必须实现InvocationHanlder) + 反射机制 生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。要求目标对象实现了接口。
2、动态字节码增强:利用ASM/CGLIB,在程序运行期间,对动态构建的字节码文件生成相应的子类,将横切逻辑加入自类中。不需要目标对象实现接口,但扩展类以及方法不能为final。
Spring AOP在无法采用动态代理机制进行AOP功能扩展的时候,会使用CGLIB库的动态字节码增强支持来实现AOP的功能扩展。
在Spring配置文件中加入
3、java代码生成
4、自定义类加载器
二、基本概念
1、JointPoint(连接点/织入点):在系统运行之前,AOP的功能模块都需要织入到OOP的功能模块中。所以,要进行这种织入过程, 我们需要知道在系统的哪些执行点上进行织入操作,这些将要在其之上进行织入操作的系统执行点就 称之为Joinpoint。
常用的JointPoint类型:
1)方法调用(Method Call)。当某个方法被调用的时候所处的程序执行点,上图1中的后面三个“圆 圈”所标记的时点都属于这种类型。
2)方法调用执行(Method Call execution) 。称之为方法执行或许更简洁,该Joinpoint类型代表的是某个方法内部执行开始时点,应该与方法调用类型的Joinpoint进行区分。
方法调用(method call)是在调用对象上的执行点,而方法执行(method execution)则是在被调用到的方法逻辑执行的时点。对于同一对象,方法调用要先于方法执行。
构造方法调用(Constructor Call)。程序执行过程中对某个对象调用其构造方法进行初始化的时点,也就是图1中如下代码所在的执行点。
HelioBean helloBean = new HelloBean( "Hello! ");
构造方法执行(Constructor Call Execution)。构造方法执行和构造方法调用之间的关系类似于方法执行和方法调用之间的关系,指的是某个对象构造方法内部执行的开始时点。
字段设置(FieldSet)。对象的某个属性通过setter方法被设置或者直接被设置的时点。该Joinpoint 的本质是对象的属性被设置,而通过setter方法设置还是直接设置触发的时点是相同的。
字段获取(FieldGet)。相对于字段设.置型的Joinpoint,字段获取型的Joinpoint,对应的是某个对象相应属性被访问的时点。可以通过getter方法访问,当然也可以直接访问。
异常处理执行(Exception Handler Execution该类型的Joinpoin说t应程序执行过程中,在某些类型异常抛出后,对应的异常处理逻辑执行的时点。
类初始化(Class initialization)»类初始化型的Joinpoint,指的是类中某些静态类型或者静态块的初始化时点。比如,如下代码中的log4j初始化位置即属于该类型Joinpoint对应位置的一种。
class FooBar{
static{
//初始化Iog4j
BasicConfigurator.configure();
}
private Foo attribute;
//其他方法定义
}
2、Pointcut(切点):Pointcui概念代表的是Joinpoint的表述方式。将横切逻辑织入当前系统的过程中,需要参照Pointcut 规定的Joinpoint信息,才可以知道应该往系统的哪些Joinpoint上织入横切逻辑
以图1中的helloBean.helloMethod()所在位置的Joinpoint为例,该方法在程序中两个地方被调用,而我们系统在这两个地方都要织入相应的横切逻辑,那么,我们就可以通过以下Pointcut表述来指定这两个Joinpoint:系统中HelloBean类的helloMechod()方法被调用的所有Joinpoint。
Pointcut的表述方式:直接指定Joinpoint所在方法名称、正则表达式、使用特定的Pointcut表述语言。
3、Advice(通知、增强处理):按照Advice在Joinpoint位置执行时机的差异或者完成功能的不同,Advice可以分成多种具体形式。
1)Before Advice
Before Advice是在Joinpoint指定位置之前执行的Advice类型。通常,它不会中断程序执行流程,但如果必要,可以通过在Before Advice中抛出异常的方式来中断当前程序流程。如果当前Before Advice 将被织入到方法执行类型的Joinpoint,那么这个Before Advice就会先于方法执行而执行。
通常,可以使用Before Advice做一些系统的初始化工作,比如设置系统初始值,获取必要系统资源等。当然,并非就限于这些情况。如果要用Before Advice来封装安全检查的逻辑,也不是不可以的,但通常情况下,我们会使用另一种形式的Advice。
2)After Advice
顾名思义,After Advice就是在相应连接点之后执行的Advice类型,但该类型的Advice还可以细分为以下三种。
After returning Advice,只有当前Joinpoint处执行流程正常完成后,After returning Advice才会执行。比如方法执行正常返回而没有抛出异常。
After throwing Advice, 又称Throws Advice,只有在当前Joinpoint执行过程中抛出异常的情况下,才会执行。比如某个方法执行类型的Joinpoint抛出某异常而没有正常返回。
After Advice ,或许叫After (Finally) Advice更为确切,该类型Advice不管Joinpoint处执行流程是正常终了还是抛出异常都会执行,就好像Java中的finally块一样。
如果以方法执行类型的Joinpoint为例,我们的各种Advice的执行时机可以基本如图3所示。
3)Around Advice
AOP Alliance属下的AOP实现大都采用拦截器(Interceptor)的叫法,但完成的功能是一样的。Around Advice对附加其上的Joinpoint进行“包裹”,可以在Joinpoint之前和之后都指定相应的逻辑,甚至于中断或者忽略Joinpoint处原来程序流程的执行。
Around Advice的行为可以因为你而发生改变。呵呵,就好像这“居心叵测”的“叵”字,中间的 那一 “口 ”就是Joinpoint,上下一横就好像是要执行的逻辑。当我们只是希望在Joinpoint之前和之后执行横切逻辑,而忽略原来Joinpoint处的逻辑执行的时候,就是居心“叵”测了。而正常情况下,Around Advice应该像一个“巨"字,我们执行完Joinpoint之前的逻辑之后,会接着走Joinpoint,然后才是Joinpoint 之后的逻辑。
既然Around Adviceoj以在Joinpoin之前和之后都能执行相应的逻辑,那么,它自然可以完成Before Advice和After Advice的功能。不过,通常情况下,还是应该根据场景选用更为具体的Advice类型。
Around Advice应用场景非常广泛,我想大家对于J2EE中的Servlet规范提供的Filter功能应该很熟悉吧。实际上,它就是Around Advice的一种体现。使用它,我们就可以完成“资源初始化”、“安全检 查”之类横切系统的关注点了。
4)Introduction
在Aspect!中称Inter-Type Declaration,在JBoss AOP中称Mix-in.都指的是这同一种类型的Advice. 与之前的几种Advice类型不同,Introduction不是根据横切逻辑在Joinpoint处的执行时机来区分的,而是根据它可以完成的功能而区别于其他Advice类型。
Introduction可以为原有的对象添加新的特性或者行为,这就好像你是一个普通公民,当让你穿军装,带军帽,添加了军人类型的Introduction之后,你就拥有军人的特性或者行为。
4、Aspect(切面):Aspect是对系统中的横切关注点逻辑进行模块化封装的AOP概念实体。通常情况下,Aspect可以包含多个Pointcut以及相关Advice定义。