Spring AOP技术

1.AOP基本介绍

AOP 的全称 (aspect oriented programming) ,面向切面编程。
1.和传统的面向对象不同。 面向切面编程是根据自我的需求,将切面类的方法切入到其他的类的方法中。(这么说抽象吧!来张图来解释。)
如图

传统的面向对象方法是 对象.方法()然后被调用

面向切面编程是什么呢?

首先满足 @1 类的这个方法是通过动态代理实现的比如有个A.say()方法。

然后有一个切面类B,这个类也有许多方法比如study(),但是不同的是,这个类中的方法study(),可以插入到A.say()的方法前后进行。 当A.say()被调用的时候,切面类B的study()方法****就会被自动调用。

这就和传统的直接调用B.study()方法不同,切面类的方法是直接插入到另外一个动态代理类的方法的前后的。

AOP的核心概念

面向切面编程(AOP)是一种编程范式,它允许开发者将横切关注点(如日志记录、事务管理、安全性等)与业务逻辑分离,从而提高代码的模块化和可维护性。在Java中,AOP通常通过使用框架如Spring来实现。

介绍AOP的关键术语

切面(Aspect):切面是封装横切关注点的模块。它包含了一组通知(Advice)和切入点(Pointcut)。
连接点(Joinpoint):在程序执行过程中的特定点,如方法的调用或执行,异常的抛出等。
切点(Pointcut):切点是定义在哪些连接点上应用通知的规则。
通知(Advice):通知是在切点上执行的代码,它定义了在连接点上执行的逻辑,如前置、后置、环绕等。
解释AOP的几种通知类型

前置(Before):在方法执行前执行的通知。
后置(After):在方法执行后执行的通知,无论方法是否成功执行。
环绕(Around):在方法调用前后都可以执行的通知,可以控制方法的调用过程。
异常(Throws):在方法抛出异常后执行的通知。
最终(AfterReturning):在方法正常返回后执行的通知。

2.AOP的快速入门

在xml配置下:

XML 复制代码
    <context:annotation-config/>
    <context:component-scan base-package="aspectj"/>
    <!--启用基于注解方式的AOP功能-->
    <aop:aspectj-autoproxy proxy-target-class="true"/>

【Spring进阶系列丨第十篇】基于注解的面向切面编程(AOP)详解_spring 切面 注解-CSDN博客

3.AOP的切入表达式

语法细节

注意第一个*是包含了任意的权限修饰符和返回值。

方法说明

注意细节

1. 切入表达式也可以指向类的方法 , 这时切入表达式会对该类 / 对象生效
2. 切入表达式也可以指向接口的方法 , 这时切入表达式会对实现了接口的类 / 对象生效
3. 切入表达式也可以对没有实现接口的类,进行切入 (因为Spring的CGLIB)。

4.JoinPoint连接点

作用:通过 JoinPoint 可以获取到调用方法的签名 signature
方法:

// 获取目标方法名

joinPoint.getSignature().getName();

// 获取目标方法所属类的简单类名
joinPoint.getSignature().getDeclaringType().getSimpleName();

// 获取目标方法所属类的类名
joinPoint.getSignature().getDeclaringTypeName();

// 获取目标方法声明类型(public、private、protected)
joinPoint.getSignature().getModifiers();

// 获取传入目标方法的参数,返回一个数组
Object[] args = joinPoint.getArgs();

// 获取被代理的对象
joinPoint.getTarget();

// 获取代理对象自己
joinPoint.getThis();

在如下代码中要将beforeMethod()方法切入到aspectj包下的SmartDog类的getSum()下

java 复制代码
@Before(value = "execution(public float aspectj.SmartDog.getSum(float, float))")
public void beforeMethod(JoinPoint joinPoint){
joinPoint.getSignature().getName(); // 获取目标方法名
joinPoint.getSignature().getDeclaringType().getSimpleName(); // 获取目标方法所属类的简单类名
joinPoint.getSignature().getDeclaringTypeName(); // 获取目标方法所属类的类名
joinPoint.getSignature().getModifiers(); // 获取目标方法声明类型(public、private、protected)
Object[] args = joinPoint.getArgs(); // 获取传入目标方法的参数,返回一个数组
joinPoint.getTarget(); // 获取被代理的对象
joinPoint.getThis(); // 获取代理对象自己
}

5.返回,异常,环绕通知返回结果

java 复制代码
//返回通知
    @AfterReturning(value = "execution(public float aspectj.SmartDog.getSum(float, float))", returning = "res")
    public void showSuccessEndLog(JoinPoint joinPoint, Object res) {
        Signature signature = joinPoint.getSignature();
        System.out.println("SmartAnimalAspect-切面类showSuccessEndLog()-方法执行正常结束-日志-方法名-" + signature.getName() + " 返回的结果是=" + res);
    }


   //异常通知
    @AfterThrowing(value = "execution(public float aspectj.SmartDog.getSum(float, float))", throwing = "throwable")
    public void showExceptionLog(JoinPoint joinPoint, Throwable throwable) {
        Signature signature = joinPoint.getSignature();
        System.out.println("SmartAnimalAspect-切面类showExceptionLog()-方法执行异常-日志-方法名-" + signature.getName() + " 异常信息=" + throwable);
    }

6.切入点表达式重用

在这些切入方法里面,如果是作用于同一个方法,就会让切入表达式感到重复和冗余,于是为了统一管理切入点表达式,可以使用切入点表达式重用技术

java 复制代码
package aspectj;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import java.util.Arrays;

/**
 * @version 1.0
 * 这是一个切面类
 */
@Aspect
@Component("smartAnimalAspect")
public class SmartAnimalAspect {

//    切入表达式重用技术
    @Pointcut(value = "execution(public float aspectj.SmartDog.getSum(float, float))")
    public void mypointcut()
    {

    }
    //给SmartDog配置前置,返回,异常,最终通知
    //解读
    //1.@Before表示我们目标对象执行前要执行的哪个方法
    //2.value = "execution(public float aspectj.SmartDog.getSum(float, float))"
    // 是指定切入到哪个类的哪个方法 形式是:访问修饰符 返回类型 全类名.方法名(形参列表) execution是执行的意思
    //  JoinPoint joinPoint 在底层执行的时候,由Aspectj切面框架,会给该切入方法传入jointPoint对象
    //  通过该对象可以获得相关的信息。
    //前置通知
    @Before(value = "mypointcut()")
    public void showBeginLog(JoinPoint joinPoint) {
        //通过连接点对象joinPoint 可以获取方法签名
        Signature signature = joinPoint.getSignature();
        System.out.println("SmartAnimalAspect-切面类showBeginLog()[使用的myPointCut()]-方法执行前-日志-方法名-" + signature.getName() + "-参数 "
                + Arrays.asList(joinPoint.getArgs()));
    }

   //返回通知
   @AfterReturning(value = "mypointcut()",returning = "res")
    public void showSuccessEndLog(JoinPoint joinPoint, Object res) {
        Signature signature = joinPoint.getSignature();
        System.out.println("SmartAnimalAspect-切面类showSuccessEndLog()-方法执行正常结束-日志-方法名-" + signature.getName() + " 返回的结果是=" + res);
    }


   //异常通知
   @AfterThrowing(value = "mypointcut()",throwing = "throwable")
    public void showExceptionLog(JoinPoint joinPoint, Throwable throwable) {
        Signature signature = joinPoint.getSignature();
        System.out.println("SmartAnimalAspect-切面类showExceptionLog()-方法执行异常-日志-方法名-" + signature.getName() + " 异常信息=" + throwable);
    }

    //最终通知
   @After(value = "mypointcut()")
    public void showFinallyEndLog(JoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        System.out.println("SmartAnimalAspect-切面类showFinallyEndLog()-方法最终执行完毕-日志-方法名-" + signature.getName());
    }

}

7.切面优先级问题

切面优先级问题 :
如果同一个方法,有多个切面在同一个切入点切入,那么执行的优先级如何控制 .
基本语法
@order(value=n) 来控制 n 值越小,优先级越高 .

举例
切面类1(优先执行)

java 复制代码
package aspectj;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.util.Arrays;

/**
 * @version 1.0
 * 这是一个切面类
 */
@Order(value = 1)
@Aspect
@Component("smartAnimalAspect")
public class SmartAnimalAspect {

//    切入表达式重用技术
    @Pointcut(value = "execution(public float aspectj.SmartDog.getSum(float, float))")
    public void mypointcut()
    {

    }

    //前置通知
//    @Before(value = "execution(public float aspectj.SmartDog.getSum(float, float))")
    @Before(value = "mypointcut()")
    public void showBeginLog(JoinPoint joinPoint) {
        //通过连接点对象joinPoint 可以获取方法签名
        Signature signature = joinPoint.getSignature();
        System.out.println("1前置通知" + signature.getName() + "-参数 "
                + Arrays.asList(joinPoint.getArgs()));
    }

   //返回通知
//    @AfterReturning(value = "execution(public float aspectj.SmartDog.getSum(float, float))", returning = "res")
   @AfterReturning(value = "mypointcut()",returning = "res")
    public void showSuccessEndLog(JoinPoint joinPoint, Object res) {
        Signature signature = joinPoint.getSignature();
        System.out.println("1返回通知" + signature.getName() + " 返回的结果是=" + res);
    }


   //异常通知
//    @AfterThrowing(value = "execution(public float aspectj.SmartDog.getSum(float, float))", throwing = "throwable")
   @AfterThrowing(value = "mypointcut()",throwing = "throwable")
    public void showExceptionLog(JoinPoint joinPoint, Throwable throwable) {
        Signature signature = joinPoint.getSignature();
        System.out.println("1异常通知" + signature.getName() + " 异常信息=" + throwable);
    }

    //最终通知
//    @After(value = "execution(public float aspectj.SmartDog.getSum(float, float))")
   @After(value = "mypointcut()")
    public void showFinallyEndLog(JoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        System.out.println("1最终通知" + signature.getName());
    }

}

切面类2(没有切面类1的优先级高)

java 复制代码
package aspectj;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.util.Arrays;

/**
 * @version 1.0
 * 这是一个切面类
 */
@Order(value = 2)
@Aspect
@Component("smartAnimalAspect2")
public class SmartAnimalAspect2 {

//    切入表达式重用技术
    @Pointcut(value = "execution(public float aspectj.SmartDog.getSum(float, float))")
    public void mypointcut()
    {

    }
    //前置通知
//    @Before(value = "execution(public float aspectj.SmartDog.getSum(float, float))")
    @Before(value = "mypointcut()")
    public void showBeginLog(JoinPoint joinPoint) {
        //通过连接点对象joinPoint 可以获取方法签名
        Signature signature = joinPoint.getSignature();
        System.out.println("2前置通知");
    }

   //返回通知
//    @AfterReturning(value = "execution(public float aspectj.SmartDog.getSum(float, float))", returning = "res")
   @AfterReturning(value = "mypointcut()",returning = "res")
    public void showSuccessEndLog(JoinPoint joinPoint, Object res) {
        Signature signature = joinPoint.getSignature();
        System.out.println( "2返回通知 返回的结果是=" + res);
    }


   //异常通知
//    @AfterThrowing(value = "execution(public float aspectj.SmartDog.getSum(float, float))", throwing = "throwable")
   @AfterThrowing(value = "mypointcut()",throwing = "throwable")
    public void showExceptionLog(JoinPoint joinPoint, Throwable throwable) {
        Signature signature = joinPoint.getSignature();
        System.out.println("2异常通知" + throwable);
    }

    //最终通知
//    @After(value = "execution(public float aspectj.SmartDog.getSum(float, float))")
   @After(value = "mypointcut()")
    public void showFinallyEndLog(JoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        System.out.println("2最终通知" + signature.getName());
    }

}

结果:

注意

不能理解成:优先级高的每个消息通知都先执行,这个和方法调用机制(和 Filter 过滤器
链式调用类似)

8.基于XML的AOP

定义SmartAnimalable接口

java 复制代码
package xml;

/**
 * @author ygd
 */
public interface SmartAnimalable {
    float getSum(float i, float j);
    float getSub(float i, float j);
}

定义SmartDog类

java 复制代码
package xml;


/**
 * @author ygd
 */
public class SmartDog implements SmartAnimalable {
    @Override
    public float getSum(float i, float j) {
        float result = i + j;
        System.out.println("getSum() 方法内部打印 result= " + result);
        return result;
    }
    @Override
    public float getSub(float i, float j) {
        float result = i - j;
        System.out.println("getSub() 方法内部打印 result= " + result);
        return result;
    }
}

定义SmartAnimalAspect切面类

java 复制代码
package xml;


import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;

import java.util.Arrays;

/**
 * @version 1.0
 * 这是一个基于XML的切面类
 */

public class SmartAnimalAspect {


    public void showBeginLog(JoinPoint joinPoint) {
        //通过连接点对象joinPoint 可以获取方法签名
        Signature signature = joinPoint.getSignature();
        System.out.println("1前置通知" + signature.getName() + "-参数 "
                + Arrays.asList(joinPoint.getArgs()));
    }


    public void showSuccessEndLog(JoinPoint joinPoint, Object res) {
        Signature signature = joinPoint.getSignature();
        System.out.println("1返回通知" + signature.getName() + " 返回的结果是=" + res);
    }



    public void showExceptionLog(JoinPoint joinPoint, Throwable throwable) {
        Signature signature = joinPoint.getSignature();
        System.out.println("1异常通知" + signature.getName() + " 异常信息=" + throwable);
    }


    public void showFinallyEndLog(JoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        System.out.println("1最终通知" + signature.getName());
    }

}

定义beans01.xml

XML 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<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 https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">

<!--配置一个切面类对象-bean-->
    <bean class="xml.SmartAnimalAspect" id="smartAnimalAspect"/>
<!--配置SmartDog对象-bean-->
    <bean class="xml.SmartDog" id="dog"/>
<!--配置切面类,一定要引入xmlns:aop命名空间-->
    <aop:config>
<!-- 注意顺序,先配置切入点,在配置切面对象-->
<!--配置切入点-->
        <aop:pointcut id="mypointcut" expression="execution(public float xml.SmartDog.getSum(float, float))"/>
<!--这里指定切面对象-->
        <aop:aspect ref="smartAnimalAspect" order="10">
<!--配置前置通知-->
            <aop:before method="showBeginLog" pointcut-ref="mypointcut"/>
<!--配置返回通知-->
            <aop:after-returning method="showSuccessEndLog" pointcut-ref="mypointcut" returning="res"/>
<!--配置异常通知-->
            <aop:after-throwing method="showExceptionLog" pointcut-ref="mypointcut" throwing="throwable"/>
<!--配置最终通知-->
            <aop:after method="showFinallyEndLog" pointcut-ref="mypointcut"/>
        </aop:aspect>
    </aop:config>
    
</beans>

注意点:要 先配置切面点,在配置切面类

xml的语法,跟上面所写的基于注解的aop道理都是一样的。

9.Spring的AOP机制是基于Spring框架中的基于JDK的动态代理和基于CGLib的动态代理

动态代理 基于jdk的Proxy(面向接口) 与spring的CGlib(面向父类)-CSDN博客

相关推荐
神仙别闹1 小时前
基于java的改良版超级玛丽小游戏
java
黄油饼卷咖喱鸡就味增汤拌孜然羊肉炒饭1 小时前
SpringBoot如何实现缓存预热?
java·spring boot·spring·缓存·程序员
暮湫2 小时前
泛型(2)
java
超爱吃士力架2 小时前
邀请逻辑
java·linux·后端
南宫生2 小时前
力扣-图论-17【算法学习day.67】
java·学习·算法·leetcode·图论
转码的小石2 小时前
12/21java基础
java
李小白662 小时前
Spring MVC(上)
java·spring·mvc
GoodStudyAndDayDayUp2 小时前
IDEA能够从mapper跳转到xml的插件
xml·java·intellij-idea
装不满的克莱因瓶3 小时前
【Redis经典面试题六】Redis的持久化机制是怎样的?
java·数据库·redis·持久化·aof·rdb
n北斗3 小时前
常用类晨考day15
java