1.AOP基本概念

切面(Aspect):通知(advice)和切入点(pointcut)共同组成了切面(aspect)

切入点(Pointcut):匹配join point的谓词,切面切向哪里,某个类或者某一层包路径

连接点(Joinpoint):aop拦截的类或者方法,例如方法被调用时、异常被抛出时。(在Spring中,所有的方法都可以认为是joinpoint,但是我们不希望所有的方法都添加Advice,而pointcut的作用就是提供一组规则来匹配joinpoint,给满足规则的joinpoint添加Advice。)

通知(Advice):切面何时使用,即注有@Around、@Before、@After等注解的方法

目标对象(Target Object):被通知的对象

AOP代理(AOP Proxy): AOP的代理有两种,一种是JDK动态代理,一种是CGLIB代理。默认情况下,TargetObject实现了接口时,则采用JDK动态代理;反之,采用CGLIB代理

Joinpoint和Pointcut区别:在Spring AOP中,所有的方法执行都是joinpoint,而pointcut是一个描述信息,它修饰的是joinpoint,通过pointcut可以确定哪些jointpoint可以被Advice。

2.通知(Advice)类型说明

@Around:环绕通知,包围一个连接点的通知,可以在核心方法前后完成自定义的行为。这是最常用最重要的。这个注解需要传入参数ProceedingJoinPoint pjp,决定是否进入核心方法—-**调用pjp.proceed();**如果不调的话将不进入核心方法!

@Before:前通知,核心代码执行前通知

@After:后通知,连接点执行退出时通知(不论正常返回还是异常退出)

@AfterReturning:返回后通知,正常返回后通知

@AfterThrowing:抛出异常后通知,抛出异常时通知

注意:除了@Around传的参数是ProceedingJoinPoint pjp外,其它都是传的JoinPoint jp,也就是说能控制是否进入核心代码的只有Around,因为aop走到核心代码就是通过调用ProceedingJoinPoint的proceed()方法,而JoinPoint没有这个方法。

3.使用注解声明切面

代码中的中文只是为了便于描述和理解,实际情况中不能使用中文!!!!

  • 目标对象

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    @Component   //Banana组件
    public class Banana {

    public void 摔跤() {
    //异常通知
    //int i = 1;
    //i=i/0;
    System.out.println("摔跤");
    }

    public String 跳舞() {
    return "我还要跳舞";
    }
    }
  • 定义切面

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    @Aspect  //表明是一个切面
    @Component //表明是一个组件
    public class Van {

    //定义公共切点,这样不用以后每一次都写一遍 切入到Banana摔跤方法
    @Pointcut("execution(* com.yoyiyi.java.Banana.摔跤())")
    public void poincut() {
    }

    //前置通知 在Banan摔跤之前说FQ
    @Before("poincut()")
    public void 前置sayFQ() {
    System.out.println("前置FQ");
    }

    //后置通知 在Banana摔跤之后说FQ
    @After("poincut()")
    public void 后置sayFQ() {
    System.out.println("后置FQ");
    }

    //异常通知 Banana 摔跤发生异常说FQ 比如没体力
    @AfterThrowing("poincut()")
    public void 异常sayFQ() {
    System.out.println("异常FQ");
    }

    //返回后通知 Van想知道Banana跳舞之后在干啥 Van不知道,所以Object
    @AfterReturning(returning = "rvt",
    pointcut = "execution(* com.yoyiyi.java.Banana.跳舞())")
    public void 返回通知sayFQ(Object rvt) {
    System.out.println("返回通知FQ,发现蕉说" + rvt.toString());
    }

    //环绕通知 Van在Banana摔跤之前之后都说FQ
    @Around("poincut()")
    public void 环绕sayFQ(ProceedingJoinPoint joinPoint) {
    try {
    System.out.println("前面FQ");
    joinPoint.proceed();//执行方法
    System.out.println("后面FQ");
    } catch (Throwable throwable) {
    System.out.println("异常FQ");
    throwable.printStackTrace();
    }
    }
  • 装配

    1
    2
    3
    4
    5
    6
    @Configuration            //相当于一个xml配置文件
    @EnableAspectJAutoProxy //表示开启AOP代理自动配置
    @ComponentScan(basePackages = "com.yoyiyi.java") //扫描com.yoyiyi.java 注解
    public class GuiChuConfig { //配置

    }

4.在XML文件中声明切面

====================================================================================================
aop配置元素                         用途
<aop:advisor>                       定义AOP通知器
<aop:after>                         定义AOP后置通知(不管被通知的方法是否执行成功)
<aop:after-returning>               定义aop返回通知
<aop:after-throwing>                定义aop异常通知
<aop:around>                        定义aop环绕通知
<aop:aspect>                        定义一个切面
<aop:aspectj-autoproxy>             启用@Aspect注解驱动的切面
<aop:before>                        定义一个AOP前置通知
<aop:config>                        顶层的AOP配置元素.大多数的<aop:*>元素必须包含在<aop:config>元素内.
<aop:declare-parents>               以透明的方式为被通知的对象引入额外的接口
<aop:pointcut>                      定义一个切点
=====================================================================================================
  • 切面目标对象

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    @Component   //Banana组件
    public class Banana {

    public void 摔跤() {
    //异常通知
    //int i = 1;
    //i=i/0;
    System.out.println("摔跤");
    }

    public String 跳舞() {
    return "我还要跳舞";
    }
    }
  • 切面类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    @Aspect  //表明是一个切面
    @Component //表明是一个组件
    public class Van {

    //前置通知 在Banan摔跤之前说FQ
    public void 前置sayFQ() {
    System.out.println("前置FQ");
    }

    //后置通知 在Banana摔跤之后说FQ
    public void 后置sayFQ() {
    System.out.println("后置FQ");
    }

    //异常通知 Banana 摔跤发生异常说FQ 比如没体力
    @AfterThrowing("poincut()")
    public void 异常sayFQ() {
    System.out.println("异常FQ");
    }

    //返回后通知 Van想知道Banana跳舞之后在干啥 Van不知道,所以Object
    public void 返回通知sayFQ(Object rvt) {
    System.out.println("返回通知FQ,发现蕉说" + rvt.toString());
    }

    //环绕通知 Van在Banana摔跤之前之后都说FQ
    public void 环绕sayFQ(ProceedingJoinPoint joinPoint) {
    try {
    System.out.println("前面FQ");
    joinPoint.proceed();//执行方法
    System.out.println("后面FQ");
    } catch (Throwable throwable) {
    System.out.println("异常FQ");
    throwable.printStackTrace();
    }
    }
  • XML配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    <aop:aspect ref="animalXmlAspect">
    <aop:pointcut id="run" expression="execution(* com.yoyiyi.java.Banana.摔跤())"/>
    <aop:before pointcut-ref="run" method="前置sayFQ"/>
    <aop:after pointcut-ref="run" method="后置sayFQ"/>
    <aop:after-returning pointcut-ref="run" method="返回通知sayFQ"/>
    <aop:after-throwing pointcut-ref="run" method="异常sayFQ"/>
    <aop:around pointcut-ref="run" method="环绕sayFQ"/>
    </aop:aspect>