Spring---AOP基本概念以及Advice5种类型的通知注解应用实例

AOP

AOP(Aspect Oriented Programming),即面向切面编程,可以说是OOP(Object Oriented Programming,面向对象编程)的补充和完善。OOP引入封装、继承、多态等概念来建立一种对象层次结构,用于模拟公共行为的一个集合。不过OOP允许开发者定义纵向的关系,但并不适合定义横向的关系,例如日志功能。日志代码往往横向地散布在所有对象层次中,而与它对应的对象的核心功能毫无关系对于其他类型的代码,如安全性、异常处理和透明的持续性也都是如此,这种散布在各处的无关的代码被称为横切(cross cutting),在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。

AOP技术恰恰相反,它利用一种称为”横切”的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为”Aspect”,即切面。所谓”切面”,简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。
首先

在Spring中提供了面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统级服务(例如审计(auditing)和事务(transaction)管理)进行内聚性的开发。

AspectJ 中几个必须要了解的概念:

Aspect: Aspect 声明类似于 Java 中的类声明,在 Aspect 中会包含着一些 Pointcut 以及相应的 Advice。

Joint point:表示在程序中明确定义的点,典型的包括方法调用,对类成员的访问以及异常处理程序块的执行等等,它自身还可以嵌套其它 joint point。

Pointcut:表示一组 joint point,这些 joint point 或是通过逻辑关系组合起来,或是通过通配、正则表达式等方式集中起来,它定义了相应的 Advice 将要发生的地方。

Advice:Advice 定义了在 pointcut 里面定义的程序点具体要做的操作,它通过 before、after 和 around 来区别是在每个 joint point 之前、之后还是代替执行的代码。
Target: 被通知的对象。通俗的说,就是与业务本身相关的原始对象。

Proxy: 向目标对象应用通知之后创建的对象。通俗的说,就是原始对象用代理对象包装了一层又一层的对象,就是图中最上面的一大坨方框。

AspectJ 支持 5 种类型的通知注解:

@Before : 前置通知, 在方法执行之前执行

@After : 后置通知, 在方法执行之后执行

@AfterRunning: 返回通知, 在方法返回结果之后执行

@AfterThrowing: 异常通知, 在方法抛出异常之后

@Around : 环绕通知, 围绕着方法执行。
环绕通知是所有通知类型中功能最为强大的, 能够全面地控制连接点. 甚至可以控制是否执行连接点.

对于环绕通知来说, 连接点的参数类型必须是 ProceedingJoinPoint . 它是 JoinPoint 的子接口, 允许控制何时执行, 是否执行连接点.

在环绕通知中需要明确调用 ProceedingJoinPoint 的 proceed() 方法来执行被代理的方法. 如果忘记这样做就会导致通知被执行了, 但目标方法没有被执行.

注意: 环绕通知的方法需要返回目标方法执行之后的结果, 即调用 joinPoint.proceed(); 的返回值, 否则会出现空指针异常

下面用实例来讲解:

//加入jar包
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>1.8.10</version>
    </dependency>
//建立Spring配置文件
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<context:component-scan base-package="com.lyf.C">
    <context:include-filter type="annotation" expression="org.aspectj.lang.annotation.Aspect"></context:include-filter>
     <!--含义是允许base-package包中使用基于面向切面的注解-->
</context:component-scan>
    <aop:aspectj-autoproxy/> <!--含义是启用Spring对aspectj切面配置的支持-->
</beans>
package com.lyf.C;

import org.springframework.stereotype.Component;

/**
 * Created by fangjiejie on 2017/4/25.
 */
@Component
public class Animal {
    public String play(String name){
        System.out.println("动物也有娱乐活动比如:"+name);
//        System.out.println(6/0);
        return "666";
    }
}
package com.lyf.C;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;

/**
 * Created by fangjiejie on 2017/4/25.
 */
@Aspect
@Order(1)//设置横切关注点的优先级顺序
public class Dog {
    /*
   * 在目标方法执行之前,执行该通知
   */
    @Before("execution(* com.lyf.C.Animal.*(..))")
    void bark1(){
        System.out.println("Dog来打哈哈---Before");
    }
    /*
     * 在目标方法执行之后(无论是否发生异常),执行该通知
     */
    @After("execution(* com.lyf.C.Animal.*(..))")
    void bark2(){
        System.out.println("Dog来打哈哈---After");
    }
    /*
     * 在目标方法正常执行之后执行的代码
     * 返回通知是可以访问目标方法的返回值的
     */
    @AfterReturning(pointcut = "execution(* com.lyf.C.Animal.*(..))",returning = "ret")//自定义的ret必须在该方法的形参中出现
        //如果注解中参数有多个,要用逗号来链接,参数都要以键值对的形式存在
    void bark3(String ret){
        System.out.println("Dog来打哈哈---AfterReturning"+"---"+ret);
    }
    /*
         * 回环通知需要携带ProceedingJoinPoint类型参数
         * 回环通知类似于动态代理的全过程:ProceedingJoinPoint类型的参数可以决定是否执行目标方法
         * 且回环通知必须有返回值,返回值即为目标方法的返回值
         */
    @Around("execution(* com.lyf.C.Animal.play(..))")
    String bark4(ProceedingJoinPoint joinPoint){
        String rs=null;
        try {
            Object os[]=joinPoint.getArgs();//可以获取目标方法的形参
            System.out.println("Dog来打哈哈---Around--before"+"---"+os[0]);
            rs=(String) joinPoint.proceed();//以插入点形式执行,执行后用rs来保留目标方法的返回值
            System.out.println("Dog来打哈哈---Around--after"+"---"+os[0]);
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
         return rs;
    }
    /*
     * 在目标方法出现异常时出现代码
     * 可以访问到异常对象.
     */
    @AfterThrowing(pointcut = "execution(* com.lyf.C.Animal.play(..))",throwing = "ex")
    public void bark5(Throwable ex){
        System.out.println("抛出异常"+ex);
    }
}
package com.lyf.C;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;

/**
 * Created by fangjiejie on 2017/4/25.
 */
@Aspect
@Order(2)
public class Cat {
    @Before("execution(* com.lyf.C.Animal.*(..))")
    void bark1(){
        System.out.println("Cat来打哈哈---Before");
    }
    @After("execution(* com.lyf.C.Animal.*(..))")
    void bark2(){
        System.out.println("Cat来打哈哈---After");
    }
    void bark3(String ret){
        System.out.println("Cat来打哈哈---AfterReturning"+"---"+ret);
    }
    @Around("execution(* com.lyf.C.Animal.play(..))")
    String bark4(ProceedingJoinPoint joinPoint){
        String rs=null;
        try {
            Object os[]=joinPoint.getArgs();//可以获取目标方法的形参
            System.out.println("Cat来打哈哈---Around--before"+"---"+os[0]);
            rs=(String) joinPoint.proceed();//以插入点形式执行,执行后用rs来保留目标方法的返回值
            System.out.println("Cat来打哈哈---Around--after"+"---"+os[0]);
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return rs;
    }
    @AfterThrowing(pointcut = "execution(* com.lyf.C.Animal.play(..))",throwing = "ex")
    public void bark5(Throwable ex){
        System.out.println("抛出异常"+ex);
    }
}
package com.lyf.C;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * Created by fangjiejie on 2017/4/25.
 */
public class Test {
    public static void main(String[] args) {
        ApplicationContext act=new ClassPathXmlApplicationContext("bean1.xml");
        Animal animal=(Animal) act.getBean("animal");
        animal.play("swim");
    }
}

执行结果:

Dog来打哈哈---Around--before---swim
Dog来打哈哈---Before
Cat来打哈哈---Around--before---swim
Cat来打哈哈---Before
动物也有娱乐活动比如:swim
Cat来打哈哈---Around--after---swim
Cat来打哈哈---After
Dog来打哈哈---Around--after---swim
Dog来打哈哈---After
Dog来打哈哈---AfterReturning---666

我们可以自行从执行结果中分析出执行顺序。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值