Spring三大核心思想之面向切面编程(AOP)

一、什么是AOP

AOP是Spring提供的关键特性之一。AOP即面向切面编程,是OOP编程的有效补充。使用AOP技术,可以将一些系统性相关的编程工作,独立提取出来,独立实现,然后通过切面切入进系统。从而避免了在业务逻辑的代码中混入很多的系统相关的逻辑——比如权限管理,事物管理,日志记录等等。

可以理解为:
在运行时,动态的 将代码 切入到 类的 指定方法。 这种编程思想就称为面向切面编程。

来举个例子吧,你写了A方法之后,老板想让你看看这段代码的执行时间。ok,那你就在A方法的前后加上代码,查看时间。。。
过了几天,你又完成了B方法之后,老板又想让看你记录这段代码的日志信息。没问题,加上
又过了几天,老板又要让你每个功能都加上日志功能。。。没过几天你就。。。
久而久之,你就发现,你的代码中会出现越来越多与业务无关的东西。后来你发现它们都是有共同点的,于是你将这些公共的代码整体抽成common方法进行调用,想办法降低耦合。但是由于是在方法中进行调用,该污染代码还是会污染,如果严格说来还是有很强的关联系的。比如说一旦涉及到部分参数的传递,那么耦合度还是很高的。
在你想解耦合且方便维护的时候,AOP出现了。
spring提供这个工具,让你能解决上述老板给你提出的这些要求,避免你修改次数太多删库跑路~

二、AOP的相关术语

1.连接点(Joinpoint)
程序执行的某个特定位置:如类开始初始化前,类初始化后,类某个方法调用前。一个类或一段代码拥有一些边界性质的特定点,这些代码中的特定点就被称为“连接点”。Spring仅支持方法的连接点,既仅能在方法调用前,方法调用后,方法抛出异常时等这些程序执行点进行织入增强。

连接点由两个信息确定:第一是用方法表示的程序执行点;第二是用相对点表示的方位。如在Test.foo()方法执行前的连接点,执行点为Test.foo(),方位为该方法执行前的位置。Spring使用切点对执行点进行定位,而方位则在增强类型中定义。

2.切点(Pointcut)
每个程序类都拥有多个连结点,如一个拥有两个方法的类,这两个方法都是连接点,既连接点是程序中客观存在的事务。当某个连接点满足指定要求时,就可以被添加Advice,成为切点,比如:

pointcut xxxPorintcut()

:execution(void H*.say*())

每个方法被调用,都只是连接点,如果某个方法属于H开头的类,而且方法一say开头,则该方法就变成了切入点。如何使用表达式定义切入点,是AOP的核心,Spring默认使用AspectJ切入点语法。

3.通知(Advice)
在连接点上执行的行为,通知提供了在AOP中需要在切入点所选择的连接点处进行扩展现有行为的手段;包括前置通知(before advice)、后置通知(after advice)、环绕通知(around advice),在Spring中通过代理模式实现AOP,并通过拦截器模式以环绕连接点的拦截器链织入通知;在AOP中表示为干什么。

4.引介(Introduction)
引介是一种特殊的增强,它为类添加一些属性和方法。这样,即使一个业务类原本没有实现某个接口,通过AOP的引介功能,我们可以动态的为该事务添加接口的实现逻辑,让业务类成为这个接口的实现类。

5.方面/切面(Aspect)
切面由切点和增强(引介)组成,它既包括了横切逻辑的定义,也包括了连接点的定义,Spring AOP就是负责实施切面的框架,它将切面所定义的横切逻辑织入到切面所指定的链接点中。

6.目标对象(Target)
被AOP框架进行增强处理的对象。如果是动态AOP的框架,则对象是一个被代理的对象。

7.织入(Weaving)
织入是将增强添加对目标类具体连接点上的过程,AOP象一台织布机,将目标类增强或引介AOP这台织布机天衣无缝的编织在一起。

8.代理(Proxy)
一个类被AOP织入增强后,就产生了一个结果类,它是融合了原类和增前逻辑的代理类。根据不同的代理方式,代理类及可能是和原类具有相同的接口的类,也可能是原类的子类,所以我们可以采用调用原类得相同方式调用代理类。

通知类型:

  • 前置通知(Before advice):
    在某连接点之前执行的通知,但这个通知不能阻止连接点之前的执行流程(除非它抛出一个异常)。
  • 后置通知(After returning advice):
    在某连接点正常完成后执行的通知:例如,一个方法没有抛出任何异常,正常返回。
  • 异常通知(After throwing advice):
    在方法抛出异常退出时执行的通知。
  • 最终通知(After (finally) advice):
    当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)。
  • 环绕通知(AroundAdvice):
    包围一个连接点的通知,如方法调用。这是最强大的一种通知类型。环绕通知可以在方法调用前后完成自定义的行为。它也会选择是否继续执行连接点或直接返回它自己的返回值或抛出异常来结束执行。

AOP简单示例

新建 Louzai 类:

@Data
@Service
public class Louzai {

    public void everyDay() {
        System.out.println("睡觉");
    }
}

添加 LouzaiAspect 切面:

@Aspect
@Component
public class LouzaiAspect {
    
    @Pointcut("execution(* com.java.Louzai.everyDay())")
    private void myPointCut() {
    }

    // 前置通知
    @Before("myPointCut()")
    public void myBefore() {
        System.out.println("吃饭");
    }

    // 后置通知
    @AfterReturning(value = "myPointCut()")
    public void myAfterReturning() {
        System.out.println("打豆豆。。。");
    }
}

applicationContext.xml 添加:

<!--启用@Autowired等注解-->
<context:annotation-config/>
<context:component-scan base-package="com" />
<aop:aspectj-autoproxy proxy-target-class="true"/>

程序入口:

public class MyTest {
    public static void main(String[] args) {
        ApplicationContext context =new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
        Louzai louzai = (Louzai) context.getBean("louzai");
        louzai.everyDay();
    }
}

输出:

吃饭
睡觉
打豆豆。。。

示例工作流程:
在这里插入图片描述
第一块就是前置处理,我们在创建 Louzai Bean 的前置处理中,会遍历程序所有的切面信息,然后将切面信息保存在缓存中,比如示例中 LouzaiAspect 的所有切面信息。

第二块就是后置处理,我们在创建 Louzai Bean 的后置处理器中,里面会做两件事情:

  • 获取 Louzai 的切面方法:首先会从缓存中拿到所有的切面信息,和 Louzai 的所有方法进行匹配,然后找到 Louzai
    所有需要进行 AOP 的方法。
  • 创建 AOP 代理对象:结合 Louzai 需要进行 AOP 的方法,选择 Cglib 或 JDK,创建 AOP 代理对象。
    在这里插入图片描述
    第三块就是执行切面,通过“责任链 + 递归”,去执行切面。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值