AOP(Aspect Orient Programming),也就是面向切面编程,作为面向对象编程的补充。面向切面编程(AOP)和面向对象编程(OOP)互为补充,面向对象编程将程序分解成各个层次的对象,而面向切面编程将程序运行过程分解成各个切面,可以这样理解,面向对象编程从静态角度考虑程序结构,面向切面编程是从动态角度考虑运行过程。AOP专门用来处理具有横切性质的系统服务,如事务管理、安全检查、缓存、对象池管理等。
切面(Aspect):业务流程运行的某个特定步骤,也就是应用运行过程中的关注点,关注点可以横切多个对象,所以常常也称为横切关注点。
连接点(Joinpoint):程序运行过程中明确的点,如方法的调用,或者异常的抛出。Spring AOP中,连接点总是方法的调用。
增强处理(Advice):AOP框架在特定在特定的切入点执行的增强处理。出来有”around”、”before”、”after”等类型。
切入点(Pointcut):可以插入增强处理的连接点。简而言之,当某个连接点满足指定要求时,该连接点将被添加增强处理,该连接点也就变成了切入点。
引入:将方法或者字段添加到被处理的类中。Spring允许引入新的接口到任何被处理的对象。
目标对象(Target):被AOP框架进行增强处理的对象,也被称为增强的对象。如果AOP框架是通过运行时代理来实现的,那么这个对象是一个被代理的对象。
AOP代理:AOP框架创建的对象,简单地说代理就是对目标对象的加强。Spring中的AOP代理可以是JDK动态代理,也可以是DBLIB代理。前者为实现接口的目标对象的代理,后者为不实现接口的目标对象的代理。
织入(Weaving):将增强处理添加到目标对象中,并创建一个被增强的对象(AOP代理)的过程就是织入。织入有两种方式:编译时增强(AspectJ)和运行时增强(DGLIB)。
AOP代理实际是由AOP框架动态生成的一个对象,该对象包含了目标对象全部的方法。AOP的方法在特定的切入点添加增强处理,并回调了目标对象的方法。
Before增强处理使用@Before来标注一个方法,需要指定一个value属性,该属性指定一个切入点表达式(既可以是一个已有的切入点,也可以直接定义切入点表达式),用于指定该增强处理将被织入哪些切入点。
实例介绍:
//定义一个Before增强处理的切面
@Aspect
public class BeforeAdviceTest {
//执行lee包下所有类的所有方法都将作为切入点
@Before("execution(* kuozhan.before.*.*(..))")
public void authority(){
System.out.println("模拟执行权限检查");
}
}
//lee类下定义一个Chiness类,该类使用@Component Annotation进行标注
@Component
public class Chiness implements Person {
//实现Person接口的sayHello方法
public String sayHello(String name) {
return name+" hello,Spring AOP!";
}
//实现Person接口的eat方法
public void eat(String food) {
System.out.println("我正在吃:"+food);
}
public static void main(String[] args){
Chiness ch=new Chiness();
System.out.println(ch.sayHello("luowz"));
ch.eat("pie");
}
}
使用Before增强处理只能在目标方法执行之前织入增强,使用Before增强处理无需理会目标方法的执行,所以Before处理无法阻击目标方法的执行。
After增强处理使用@AfterReturning来标注一个AfterReturning增强处理,目标方法完成后将被织入。需要指定的两个常用的属性:
pointcut/value:两个属性作用是一样的,用于指定切入点对应的切入表达式。当指定pointcut属性值后,value属性值将被覆盖。
returning:指定一个返回值形参名,增强处理定义的方法可通过该形参名来访问目标方法的返回值。
@Aspect
public class AfterReturningTest {
//执行lee包下所有类的所有方法都将作为切入点
@AfterReturning(pointcut="execttion(* kuozhan.after.*.*(..))",returning="rvt")
public void log(Object rvt){
System.out.println("获取目标对象方法返回值:"+rvt);
System.out.println("模拟日记积累功能。。。");
}
}
使用returning属性可用于标注一个额外的作用:它可用于限定切入点只匹配具有对应返回值类型的方法。如上例中log()方法中定义rvt形参的类型为String,则该切入点只匹配lee包下返回值类型为String的 所有方法。
AfterThrowing增强处理使用@AfterThrowing标注一个方法,主要用于处理程序中未处理的异常。常用属性有:
pointcut/value:指定切入点对应的切入表达式
throwing:指定一个返回值形参名,增强处理定义的方法可通过该形参名来访问目标方法中所抛出的异常对象
//定义一个切面
@Aspect
public class AfterThrowingAdviceTest
{
//执行lee包下所有类的、所有方法都将作为切入点
@AfterThrowing(pointcut="execution(* lee.*.*(..))"
, throwing="ex")
public void doRecoveryActions(Throwable ex)
{
System.out.println("目标方法中抛出的异常:" + ex);
System.out.println("模拟抛出异常后的增强处理...");
}
}
@Component
public class Chinese implements Person
{
//实现Person接口的sayHello()方法
public String sayHello(String name)
{
//该方法体内虽然抛出了异常,但该方法
//自己处理了该异常,所以AOP不会对该异常进行处理
try
{
System.out.println("sayHello方法开始被执行...");
new FileInputStream("a.txt");
}
catch (Exception ex)
{
System.out.println("目标类的异常处理"
+ ex.getMessage());
}
//返回简单的字符串
return name + " Hello , Spring AOP";
}
//定义一个divide()方法
public void divide()
{
int a = 5 / 0;
System.out.println("divide执行完成!");
}
}
sayHello()方法和divide()方法都会抛出异常,但sayHello()方法已经用catch语句显示地处理,Spring AOP不会处理该异常;而divide()方法将抛出一个ArithmeticExceptionyic ,且没有任何程序处理,故Spring AOP将会对该异常进行处理。
注意:throwing属性还用于限定切入点只匹配指定类型的异常。如doRecoveryActions()方法中定义的ex形参的类型是NullPointerException,则该切入点只匹配抛出类型为NullPointerException的异常情况。catch捕捉到的异常会完成处理该异常,若catch块中没有重新抛出新异常,则该方法可以正常结束;而AfterThrowing处理异常虽然处理了该异常,但它不能完成处理该异常,还会传播到上一级调用者。
After增强处理用@After进行标注,不论方法如何结束(正常的或抛异常的),After都会被织入目标对象中,因此After必须准备正常返回和异常返回两种情况,这种增强适合处理释放资源。需要指定一个属性value,指定增强处理被织入的切入点。
//定义一个切面
@Aspect
public class AfterAdviceTest
{
//执行lee包下所有类的、所有方法都将作为切入点
@After("execution(* lee.*.*(..))")
public void release()
{
System.out.println("模拟方法结束后的释放资源...");
}
}
Around增强处理使用@Around标注一个方法,它相当Before增强处理和After增强处理的总和,既可以在执行目标方法是织入增强处理,也可以执行目标方法之后织入增强处理。Around增强处理可以决定目标方法什么时候执行,如何执行,甚至可以完全阻止目标方法的执行,还可以改变执行目标方法的参数值,改变执行目标方法之后的返回值。Around是功能最强大的增强处理,但通常需要在线程安全的环境下使用,消耗资源比较大。Around需要指定一个属性value,指定增强处理被织入的切入点。
定义一个Around增强处理第一个形参必须是ProceedingJoinPoint类型(至少一个形参),在增强处理方法体内,调用ProceedingJoinPoint的proceed()方法才会执行目标方法―这是Around增强处理可以完全控制目标对象执行的时机、如何执行的关键;如果程序没有调用,目标方法不会被执行,proceed()方法执行时,可以传入一个Object[]对象,该数组的值将被传如目标方法作为执行的实参。
//定义一个切面
@Aspect
public class AroundAdviceTest
{
//执行lee包下所有类的、所有方法都将作为切入点
@Around("execution(* lee.*.*(..))")
public Object processTx(ProceedingJoinPoint jp)
throws java.lang.Throwable
{
System.out.println("执行目标方法之前,模拟开始事务...");
//执行目标方法,并保存目标方法执行后的返回值
Object rvt = jp.proceed(new String[]{"被改变的参数"});
System.out.println("执行目标方法之后,模拟结束事务...");
return rvt + " 新增的内容";
}
}
当调用ProceedingJoinPoint的proceed()方法时,传入的Object[]参数值将作为目标方法的参数,如果传入的Object[]数字长度与目标方法所需的参数个数不相等,或者参数的类型不一致,程序将会抛出异常。
本文出自 “11034391” 博客,转载请与作者联系!