Spring AOP

是 Spring 框架的一个核心模块,基于面向切面编程(AOP)思想,用于将横切关注点(如日志、事务、异常处理)从业务逻辑中分离出来,实现代码解耦。
它是高层应用框架,底层依赖 Java 代理技术(JDK 动态代理或 CGLIB),但提供了更丰富的功能和更简洁的使用方式。

2025-09-14T15:38:35.png
2025-09-14T15:39:02.png

XML配置

写一个组件@Component

比如是置前通知

这里的JoinPoint joinPoint是切入点可以获取被切入的方法的一些消息,比如,方法名signature.getName(),参数列表joinPoint.getArgs();
    public void beforeLog(JoinPoint joinPoint){
        Signature signature=joinPoint.getSignature();
        System.out.println("方法名称;"+signature.getName());
        Object[] args=joinPoint.getArgs();
        for(Object o:args){
            System.out.println("参数: " +o);
        }
        System.out.println("执行前================");
    }
如果是异常或者返回值后接入的话们可以带返回值入参,Object result,和异常错误入参Throwable e
public void after_ReturnLog(JoinPoint joinPoint,Object result)
public void after_ThrowLog(JoinPoint joinPoint,Throwable e)

切入方法

比如一下类的eat(String name)方法

@Service
public class StudentService {
    public String eat(String name){
        System.out.println("正在吃:" + name);
        return "返回值:" + name;
    }
    public void sleep(String name){
        System.out.println("正在睡:" + name);
    }
    public void run(String name){
        System.out.println("正在跑:" + name);
    }
    public void jump(String name){
        System.out.println("正在跳:" + name);
    }
}

配置xml文件
2025-09-14T15:50:45.png

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"

       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       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.test"/>
    <aop:config>
        <aop:aspect ref="logAdvice">

            <aop:before method="beforeLog" pointcut="execution(* com.test.Service.StudentService.eat(String))"/>
            <aop:after method="afterLog" pointcut="execution(* com.test.Service.StudentService.eat(String))"/>
            <aop:after-returning method="after_ReturnLog" pointcut="execution(* com.test.Service.StudentService.eat(String))" returning="result"/>
            <aop:after-throwing method="after_ThrowLog" pointcut="execution(* com.test.Service.StudentService.eat(String))" throwing="e"/>

        </aop:aspect>

    </aop:config>

</beans>

扫描组件

<context:component-scan base-package="com.test"/>

开始配置aop

<aop:config> </aop:config>

定义一个「切面」

<aop:aspect ref="logAdvice">
作用:定义一个「切面」,ref="logAdvice" 表示这个切面的逻辑由 Spring 容器中名为 logAdvice 的 Bean 提供(即你的 LogAdvice 类,需要用 @Component 或 XML 配置注入容器)。
切面(Aspect):存放通用逻辑(如日志)的类,这里就是 LogAdvice 类。

四个通知标签

<aop:before>(前置通知)

作用:在「目标方法执行前」自动调用 logAdvice 类中的 beforeLog 方法。
参数说明:
method="beforeLog":LogAdvice 类中必须有一个 beforeLog() 方法(可以带参数,需配合切入点表达式传递)。
pointcut:切入点表达式,定义 “要对哪个方法生效”。这里的 execution( com.test.Service.StudentService.eat(String)) 表示:只对 com.test.Service.StudentService 类中,参数为 String 类型的 eat 方法生效。 表示任意返回类型
...

其它,如返回后通知,或者异常报错通知,需要在后面再次增加参数如returning="result",throwing="e"

            <aop:after-returning method="after_ReturnLog" pointcut="execution(* com.test.Service.StudentService.eat(String))" returning="result"/>
            <aop:after-throwing method="after_ThrowLog" pointcut="execution(* com.test.Service.StudentService.eat(String))" throwing="e"/>

2025-09-14T16:03:26.png

指定包指定类下面的所有方法,比如UserService类下面的所有方法
            <aop:before method="logbefore" pointcut="execution(* com.xy.Service.UserService.*(..))"></aop:before>
指定包所有类下面的所有方法,比如UserService类下面的所有方法
 <aop:before method="logbefore" pointcut="execution(* com.xy.Service.*.*(..))"></aop:before>
抽取切点表达式,后续使用pointcut-ref引入
    <context:component-scan base-package="com.xy"/>
    <aop:config>

        <aop:pointcut id="pointcut1" expression="execution(* com.xy.Service.*.*(..))"/>

        <aop:aspect ref="logAdvice">
            <aop:before method="logbefore" pointcut-ref="pointcut1"></aop:before>
        </aop:aspect>
    </aop:config>

方便后续使用

<aop:config>
    <!-- 单独定义切入点:一次定义,全局复用 -->
    <aop:pointcut id="pointcut1" expression="execution(* com.xy.Service.*.*(..))"/>
    
    <aop:aspect ref="logAdvice">
        <!-- 前置通知:引用已定义的切入点 -->
        <aop:before method="logbefore" pointcut-ref="pointcut1"/>
        <!-- 后置通知:直接引用同一个切入点(无冗余) -->
        <aop:after method="logafter" pointcut-ref="pointcut1"/>
    </aop:aspect>
</aop:config>

环绕通知

配置xml

aop:around
<aop:around method="aroundLog" pointcut-ref="pointcut1"></aop:around>

编写环绕通知切入方法

    public Object aroundLog(ProceedingJoinPoint joinPoint){
        System.out.println("环绕通知=========");
        Signature signature=joinPoint.getSignature();
        System.out.println("被切入的方法是"+signature.getName());
        Object[] args=joinPoint.getArgs();
    try{
        System.out.println("【前置通知】====");
        Object result=joinPoint.proceed(args);
        System.out.println("【后置通知】====");
        System.out.println("方法返回结果  "+result);
        System.out.println("中间其余操作");
        System.out.println("【最终通知】====");
    return result;

    }catch (Throwable e){
    System.out.println("【异常通知】====");
        e.printStackTrace();
    }
    return null;
    }
环绕类型接收的参数为ProceedingJoinPoint类型
joinPoint.proceed(args);为执行该方法
执行以后就触发后,可写后置通知
后置通知后,可以写一些其它代码,然后再最终通知;
如果异常的话,会到catch (Throwable e){}代码块通知
2025-09-15T02:50:56.png
2025-09-15T02:51:18.png
环绕的异常通知
2025-09-15T03:03:10.png

注解配置

2025-09-15T07:02:19.png

前置通知
@Before("pointCut()")
最终通知,方法正常返回以后才通知
@AfterReturning(value = "pointCut()",returning = "result")
后置通知
@After("pointCut()")
异常通知
@AfterThrowing(value="pointCut()",throwing="th")
环绕类型
@Around(value = "pointCut()")

步骤

在XML配置注解支持

    <aop:aspectj-autoproxy/>

2025-09-15T06:52:43.png
2025-09-15T07:20:04.png
异常通知
2025-09-15T07:23:05.png
完整代码

@Component
@Aspect
public class LogAdvice2 {

    @Pointcut("execution(* com.xy.Service.UserService.*(..))")
    public void pointCut(){}

    @Before("pointCut()")
    public void before(JoinPoint joinPoint){
        System.out.println("-----前置通知-----");
        Signature signature=joinPoint.getSignature();
        System.out.println("被切入的方法是"+signature.getName());
        Object[] args=joinPoint.getArgs();
        System.out.println("参数:"+ Arrays.deepToString(args));
    }


    @AfterReturning(value = "pointCut()",returning = "result")
    public void returning(JoinPoint joinPoint,Object result){
        System.out.println("-----最终通知-----");
        Signature signature=joinPoint.getSignature();
        System.out.println("被切入的方法是"+signature.getName());
        Object[] args=joinPoint.getArgs();
        System.out.println("参数:"+ Arrays.deepToString(args));
        System.out.println("返回值->【"+result+"】");
    }



    @After("pointCut()")
    public void after(JoinPoint joinPoint){
        System.out.println("-----后置通知-----");
        Signature signature=joinPoint.getSignature();
        System.out.println("被切入的方法是"+signature.getName());
        Object[] args=joinPoint.getArgs();
        System.out.println("参数:"+ Arrays.deepToString(args));
    }


    @AfterThrowing(value="pointCut()",throwing="th")
    public void throwingn(JoinPoint joinPoint,Throwable th){
        System.out.println("-----异常通知-----");
        Signature signature=joinPoint.getSignature();
        System.out.println("被切入的方法是"+signature.getName());
        Object[] args=joinPoint.getArgs();
        System.out.println("参数:"+ Arrays.deepToString(args));
        System.out.println("错误信息->【"+th+"】");
    }


}

不使用XML开启Aop

编写配置类

@Configuration
@EnableAspectJAutoProxy
@ComponentScan(basePackages={"com.xy"})
public class AopConfig {
}

引入Bean

    @Test
    public void test1(){
        AnnotationConfigApplicationContext context=new AnnotationConfigApplicationContext(AopConfig.class);
        UserService userService=context.getBean(UserService.class);
        userService.play("孙悟空");
    }

环绕类型

    @Around(value = "pointCut()")
    public Object around(ProceedingJoinPoint joinPoint){
        Signature signature=joinPoint.getSignature();
        System.out.println("方法名称"+signature.getName());
        Object[] args=joinPoint.getArgs();
        System.out.println("参数:"+Arrays.deepToString(args));
        System.out.println("---------------------------");
        try{
            System.out.println("前置通知");
           Object result= joinPoint.proceed(args);
             System.out.println("最终通知");
           return  result;
        }catch (Throwable e){
            System.out.println(e);
            System.out.println("异常通知");
        }finally {
            //后置通知需要加Finally
            System.out.println("后置通知");
        }

        return null;
    }

2025-09-15T07:53:52.png