Spring 的 AOP

来源:互联网 发布:手机可以做淘宝网店吗 编辑:程序博客网 时间:2024/04/30 16:58

AOP:面向切面编程,常常被用于处理系统中分布于各个模块(不同方法)中的交叉关注点的问题,在 JavaEE 应用中,常常通过 AOP 来处理一些具有横切性质的系统级服务,如:事务管理、安全检查、缓存、对象池管理等。AOP 从程序的角度考虑程序的流程,提取业务处理过程的切面,AOP 框架并不与特定的代码耦合,它能处理程序执行中的特定的切入点,而并不与某个具体类耦合。

1、AOP 的基本概念

AOP 框架的两个特征:
① 各个步骤之间良好的隔离性。
② 源代码无关性。

切面(Aspect):切面用于组织多个 Advice, Advice 放在切面中定义。
连接点(Joinpoint):在 Spring AOP 中连接点总是方法的调用。
增强处理(Advice):AOP 框架在特定的切入点执行的增强处理。
切入点(Pointcut):可以插入增强处理的连接点。即满足要求的连接点将被添加增强处理,然后就成为了切入点。
AOP 代理:AOP 框架创建的对象,即代理就是对目标对象的加强。

2、Spring 的 AOP 支持

Spring AOP 采用基于代理的 AOP 实现方案,通常和 Spring IoC 容器一起使用。在 AOP 编程中的关键就是定义切入点和定义增强处理,一旦定义了适合的切入点和增强处理,AOP 框架会自动生成 AOP 代理。Spring 默认使用 Java 动态代理来创建 AOP 代理,这样可为任何接口实例创建代理了。

Spring AOP 中的 JDK 动态代理和 cglib 代理
如果目标对象实现了接口,默认情况下会采用 JDK 动态代理实现 AOP,所以在获取的时候应该用接口去接收拿到的对象。
如果目标对象实现了接口,可以强制使用 CGLIB 实现 AOP
如果目标兑现没有实现接口,必须采用 CGLIB 库,Spring 会自动在 JDK 动态代理和 CGLIB 代理之间切换,可以直接使用获取对象去接收。

3、基于注解的方式来定义切入点和增强处理

为了启用 Spring 对 @AspectJ 切面配置的支持,并保证 Spring 容器中的目标 Bean 被一个或多个切面自动增强,必须在 Spring 配置文件中增加下面的配置:

<!-- 启动 AspectJ 支持 --><aop:aspectj-autoproxy />

3.1 定义切面的 Bean

当启动了 @AspectJ 支持后,只要在 Spring 容器中配置一个带 @Aspect 注解的 Bean, Spring 将会自动识别该 Bean,并将 Bean 作为切面处理。

当使用 @Aspect 来修饰一个 Java 类后,Spring 将不会把该 Bean 当成组件 Bean, 处理负责自动增强的后处理 Bean 将会略过该 Bean。

/** * Created by Hinbo on 2016/9/8. * 定义切面 Bean */@Aspectpublic class LogAspect {}

3.2 定义 Before 增强处理

在一个切面类里使用 @Before 来修饰一个方法,该方法将在目标方法执行之前进行增强,使用该注释通常需要制定一个 value 属性值制定一个切入点的表达式。

/** * Created by Hinbo on 2016/9/8. * 定义切面 Bean */@Aspectpublic class LogAspect {    @Before("execution(* com.sunsharing.springDemo.service.impl.*.*(..))")    public void beforeAOP(){        System.out.println("执行 before。。。");    }}

如果 Before 增强处理没有进行特殊的处理,目标方法总会自动执行,如果想要阻止目标方法的自动执行,可以通过抛出一个异常来实现。

3.3 定义 AfterReturning 增强处理

@AfterReturning 可修饰 AfterReturning 增强处理,AfterReturning 增强处理将在目标方法正常完成后执行。

@AfterReturning 可指定两个常用属性:
① pointcut/value:用于指定该切入点对应的切入表达式,当指定了 pointcut 属性值后,value 的属性值将被覆盖。
② returning:指定一形参,该形参用于访问目标方法的返回值。

@AfterReturning(returning = "rvt", pointcut = "execution(* com.sunsharing.springDemo.service.impl.*.*(..))")public void afterReturning(Object rvt){    System.out.println("获取目标方法的返回值:"+rvt);    System.out.println("afterReturning ......");}

程序中指定一个属性值为 rvt,表明在 Advice 方法中定义名为 rvt 的形参,程序可通过 rvt 形参来访问目标方法的返回值。虽然 AfterReturning 增强处理可以访问到目标方法的返回值,但它不可以改变目标方法的返回值。

3.4 定义 AfterThrowing 增强处理

AfterThrowing 增强处理主要用于处理程序中未处理的异常。指定的两个常用属性和 AfterReturning 类似,throwing 属性指定一个形参名。

@AfterThrowing(throwing = "ex", pointcut = "execution(* com.sunsharing.springDemo.service.impl.*.*(..))")public void afterThrowing(Throwable ex){    System.out.println("目标方法中抛出的异常:"+ex);    System.out.println("模拟 Advice 对异常的修复...");}

使用 throwing 属性还有一个作用:它可用于限定切入点只匹配指定类型的异常,如果类型为 Throwable 则表明该切入点可匹配抛出的任何异常。
AOP 的 AfterThrowing 处理和直接使用 catch 捕捉不同, catch 捕捉完全处理该异常,如果 catch 块中没有抛出新的异常,则方法可以正常结束,而 AfterThrowing 不能完全处理异常,该异常仍然会提交到上一层的调用者。

3.5 After 增强处理

After 增强处理和 AfterReturning 增强处理有点类似,AfterReturning 增强处理只有在目标方法成功完成后才会被织入, After 增强处理不管目标方法是否成功结束都会被织入。使用 @After 注解时需要指定一个 value 属性指定切入点。

@After("execution(* com.sunsharing.springDemo.service.impl.*.*(..))")public void release(){    System.out.println("执行 after 去释放资源...");}

After 增强处理的作用类似于异常处理中的 finally 块的作用,无论如何都会在方法执行结束后被执行,可以用于资源的回收。

3.6 Around 增强处理

Around 增强处理近似于 Before 和 AfterReturning 的总和,Around 增强处理可以在目标方法执行之前织入,也可在目标方法之后织入。

Around 增强处理可以改变执行目标方法的参数值,也可改变执行目标方法之后的返回值。

在定义 Around 增强处理方法时,该方法至少包含一个参数 ProceedingJoinPoint 类型的参数,如果程序没有调用 ProceedingJoinPoint 参数的 proceed() 方法,则目标方法不会执行。

@Around("execution(* com.sunsharing.springDemo.service.impl.*.*(..))")public Object around(ProceedingJoinPoint joinPoint){    System.out.println("执行目标方法之前......");    try {        joinPoint.proceed();    } catch (Throwable throwable) {        throwable.printStackTrace();    }    System.out.println("执行目标方法之后......");    return 1;}

3.7 访问目标方法的参数

在定义增强处理方法时可以将第一个参数定义为 JoinPoint 类型,当增强处理方法被调用时该参数就代表了织入增强处理的连接点。

几个常用的方法:
① getArgs():返回执行目标方法时的参数
② getSignature():返回被增强的方法的相关信息
③ getTarget():返回被织入增强处理的目标对象
④ getThis():返回 AOP 框架为目标对象生成的代理对象

3.8 定义切入点

对于相同的切入点表达式可以提出来单独定义达到复用的效果,切入点的定义包含两个部分:
① 一个切入点表达式, 使用 @Pointcut 注解来标注
② 一个包含名字和任意参数的方法签名作为切入点的名称,方法的返回值必须为 void 。

@Pointcut("execution(* com.sunsharing.springDemo.service.impl.*.*(..))")public void myPointcut(){}

然后可以直接在增强处理方法定义时像调用方法一样直接调用切入点。

@Around("myPointcut()")

在使用其他切面类中的切入点时,应该使用切面类作为前缀来限制切入点。

0 0