Spring Aop

本文详细介绍了面向切面编程(AOP)与面向对象编程(OOP)的区别,重点阐述了AOP的核心概念,如切面、连接点、通知等,并通过一个具体的Spring AOP代码示例解释了如何定义和使用切面。同时,解析了@EnableAspectJAutoProxy注解的作用,以及InstantiationAwareBeanPostProcessor在AOP中的角色,展示了AOP在方法调用过程中的执行顺序。
摘要由CSDN通过智能技术生成

Spring Aop

1、OOP 和 AOP 的对比

  • OOP(Object Oriented Programming,面向对象编程

OOP适合开发者定义纵向的关系,但并不适合定义横向的关系,例如日志功能,日志代码往往横向地散布在所有对象层次中,而与它对应的对象的核心功能毫无关系。这种散布在各处的无关的代码,在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用,于是引出AOP技术来解决这个问题。

  • AOP(Aspect Oriented Programming),即面向切面编程

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

AOP把软件系统分为两个部分:
核心关注点 和 横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处基本相似

2、AOP核心概念

  • 1、横切关注点 :对哪些方法进行切入,对哪些方法进行拦截,拦截后怎么处理,这些关注点称之为横切关注点
  • 2、切面(aspect) 把原来糅杂在业务逻辑代码中的非业务代码抽取出来,(把功能相同的放在一个类中形成一个切面)类是对物体特征的抽象,切面就是对横切关注点的抽象
  • 3、连接点(joinpoint)(需要切入的点)被拦截到的点,因为Spring只支持方法类型的连接点,所以在Spring中连接点指的就是被拦截到的方法,实际上连接点还可以是字段或者构造器
  • 4、切入点(pointcut) 对连接点进行拦截的定义
  • 5、通知(advice) 所谓通知指的就是指拦截到连接点之后要执行的代码,通知分为前置、后置(finally)、异常、返回、环绕通知五类
  • 6、目标对象:代理的目标对象
  • 7、织入(weave) 将切面应用到目标对象并导致代理对象创建的过程8、引入(introduction)在不修改代码的前提下,引入可以在运行期为类动态地添加一些方法或字段

3、 代码例子:

package com.tuling;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import java.util.Arrays;
//后置通知总是会执行,它是在finally块执行
    //catch 执行异常通知
@Aspect
@Component
public class TulingLogAspect {

//拦截到 com.tuling.TulingCalculate 这个类下的函数进行织入
    @Pointcut("execution(* com.tuling.TulingCalculate.*(..))")
    public void pointCut(){};
    
 	//@After(value = "pointCut()")
    //@AfterReturning(value = "pointCut()",returning = "result")
    //@AfterThrowing(value = "pointCut()")
    @Before(value = "pointCut()")
    public void methodBefore(JoinPoint joinPoint){

        String methodName = joinPoint.getSignature().getName();
        System.out.println("执行目标方法【"+methodName+"】之前执行<前置通知>,入参"+ Arrays.asList(joinPoint.getArgs()));
    }
}

//需要在这个类的方法执行前 执行上面Aspect的方法。
package com.tuling;
import org.springframework.aop.framework.AopContext;
public class TulingCalculate {
	//add方法
    public int add(int numA, int numB) {
        System.out.println("执行目标方法:add");
        //System.out.println(1/0);
        return numA+numB;
    }
    //mod方法内调用了add方法
    public int mod(int numA,int numB){
        System.out.println("执行目标方法:mod");
        //这种写法就可以触发mod方法的切面 和add方法的切面
        int retVal = ((Calculate) AopContext.currentProxy()).add(numA,numB);

        //只会触发mod方法的切面,add方法的切面没有触发
        //int retVal = this.add(numA,numB);
        return retVal%numA;
    }
}

//测试类
package com.tuling;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import java.util.HashMap;
public class TulingMainClass {
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(MainConfig.class);
        Calculate calculate = (Calculate) ctx.getBean("calculate");
         int retVal = calculate.add(3,6);
        //int retVal = calculate.mod(2,4);
        System.out.println("运算结果是:"+retVal);
    }
}


执行结果:
在这里插入图片描述

注意:如果按照下图这样写代码,当测试类执行mod方法时,mod里面调用add方法,但是执行add方法之前没有去执行切面的代码。
在这里插入图片描述

注意:如果按照下图这样写代码,会执行add方法之前的切面代码。
在这里插入图片描述

4、注解 @EnableAspectJAutoProxy 帮我们做了什么

  • 1、 @EnableAspectJAutoProxy=====> 导入了AnnotationAwareAspectJAutoProxyCreator组件

  • 2、我们分析AnnotationAwareAspectJAutoProxyCreator 的继承关系图,发现了他具有InstantiationAwareBeanPostProcessor 的特性 和 BeanPostProcessor 接口特性
    在这里插入图片描述

  • 3、我们发现InstantiationAwareBeanPostProcessor 在实例化之前(调用构造方法之前)执行before方法
    在这里插入图片描述

5、InstantiationAwareBeanPostProcessor 的before做了啥

  • 在创建第一个bean 根据Bean的生命周期中的createBean的环节 resolveBeforeInstantiation 触发我的InstantiationAwareBeanPostProcessor的before方法.
    此时在这个环节就会去把我们的切面信息(@AspectJ)的信息找出来 然后 进行缓存,缓存内容如下图:
    在这里插入图片描述

6、BeanPostProcessor 的 after 做了啥

创建要切的对象的时候,根据切面类名去找自己的切面(增强器),然后把被切的对象 和增强器 创建成一个代理对象
在这里插入图片描述

7、代理对象怎么调用我们方法

代理对象调用:(默认jdk代理 )@proxyTragetClass为true 则为cglib代理
通过职责链模式+递归的来进行调用
在这里插入图片描述

  • 1、先执行我们的异常通知…(catch里面执行异常通知的方法)
  • 2、返回通知:(由于正是在这里返回通知中没有进行任何的try catch处理,那么加入抛出异常 就不会执 - 行返回通知的方法而是直接执行到异常通知了)
  • 3、后置通知:正是因为在后置通知中,代码在finally里中 所以他才是总是被执行的…
  • 4、前置通知:执行我们的前置通知.
  • 5、递归终止条件满足:返回去的时候逆序执行我们目标方法,就是前置,后置,返回,异常
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值