Java-Spring入门指南(十一)代理模式与Spring AOP实战

Java-Spring入门指南(十一)代理模式与Spring AOP实战


前言

在前一篇博客中,我们掌握了Spring纯Java类配置的技巧,彻底摆脱了XML的束缚。

  • 而Spring中另一项核心技术------面向切面编程(AOP),能让我们在不修改原有业务代码的前提下,为方法添加额外功能(如日志、事务、权限校验等)。

我的个人主页,欢迎来阅读我的其他文章
https://blog.csdn.net/2402_83322742?spm=1011.2415.3001.5343

我的Java-Spring入门指南知识文章专栏
欢迎来阅读指出不足
https://blog.csdn.net/2402_83322742/category_13040333.html?spm=1001.2014.3001.5482


Spring的官方AOP讲解网站
https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#aop

一、代理模式

代理模式的核心是:通过"代理对象"代替"真实对象",在不修改真实对象的前提下,为其添加额外功能

我们用房东中介租客的例子来列举一下

  • 房东(真实对象):有房子要租,但不想直接和租客打交道,还想在租房前后做额外操作(如签合同、收押金)。
  • 中介(代理对象):代替房东与租客交互,能在"租客租房"的前后添加自己的操作(如带看、收中介费)。
  • 租客(调用者):只需和中介交互,就能完成租房,无需关心房东的细节。

1.1 静态代理

静态代理需要手动为每个真实对象编写对应的代理类,步骤如下:

步骤1:定义租房接口(抽象行为)

java 复制代码
// 租房接口:定义租房的核心行为
public interface Rent {
    void rentHouse(); // 租房方法
}

步骤2:实现房东类(真实对象)

java 复制代码
// 房东:真实对象,实现租房接口
public class Landlord implements Rent {
    @Override
    public void rentHouse() {
        System.out.println("房东:我的房子成功租出去了");
    }
}

步骤3:编写中介类(代理对象)

java 复制代码
// 中介:代理对象,也实现租房接口
public class Mediator implements Rent {
    private Landlord landlord; // 持有真实对象(房东)的引用

    public Mediator(Landlord landlord) {
        this.landlord = landlord;
    }

    @Override
    public void rentHouse() {
        // 租房前:中介的额外操作
        System.out.println("中介:带租客看房,收取中介费");
        // 调用真实对象的核心方法
        landlord.rentHouse();
        // 租房后:中介的额外操作
        System.out.println("中介:协助签合同,收取押金");
    }
}

步骤4:测试静态代理

java 复制代码
public class ProxyTest {
    public static void main(String[] args) {
        // 1. 创建真实对象(房东)
        Landlord landlord = new Landlord();
        // 2. 创建代理对象(中介),并传入房东
        Mediator mediator = new Mediator(landlord);
        // 3. 通过中介租房(调用代理对象的方法)
        mediator.rentHouse();
    }
}

运行结果:

静态代理的优缺点

  • 优点:逻辑直观,能在不修改真实对象的前提下,为其添加额外功能。
  • 缺点 :代理类与真实对象绑定,若有100个真实对象,就得写100个代理类,代码冗余且维护成本高。

1.2 动态代理

动态代理无需手动编写代理类,而是运行时自动生成代理对象(常见的有JDK动态代理------基于接口、CGLIB动态代理------基于子类)。

但动态代理的底层代码较为复杂,而Spring AOP帮我们封装了这些细节,让我们能更简单地使用AOP。


二、AOP是什么?

静态代理的"一对一"绑定方式,在复杂业务中会变得非常繁琐。而AOP(Aspect Oriented Programming,面向切面编程) 正是为解决这个问题而生:它能通过"切面",对多个类的多个方法 统一添加增强功能,完全无需手动编写代理类。

2.1 AOP是什么?

AOP是一种编程思想 ,核心逻辑是:将与核心业务无关,但又分散在多个业务中的通用功能(如日志、事务、权限),抽取成"切面(Aspect)",在合适的时机(如方法执行前/后),动态"织入"到业务方法中

简单来说:业务代码只关注核心逻辑,通用功能交给AOP统一处理

2.2 AOP有什么用?

AOP专为解决"横切关注点"问题而生:

  • 横切关注点:与业务逻辑无关,但需在多个业务方法中重复出现的逻辑(如日志打印、事务控制、异常处理、权限校验等)。
  • AOP的价值:让横切关注点与业务逻辑解耦,只需写一次,就能作用于多个方法,既减少代码冗余,又便于统一维护。

2.3 AOP的使用场景是什么?

AOP的典型应用场景包括:

  • 日志记录:方法执行前/后自动记录日志。
  • 事务管理:方法执行前开启事务,执行后提交/回滚事务。
  • 权限校验:方法执行前校验用户权限。
  • 性能监控:统计方法的执行时间。
  • 异常处理:统一捕获和处理方法中的异常。

2.4 AOP的核心特点是什么?

  • 切面(Aspect):横切关注点的封装(如"日志切面类"),包含"何时增强""增强哪些方法""做什么增强"。
  • 通知(Advice):切面中的具体增强逻辑(如"方法执行前打印日志"),分为前置通知(before)、后置通知(after)、环绕通知(around)等。
  • 连接点(Join Point) :程序中可以被增强的点(Spring AOP中主要指方法调用)。
  • 切入点(Pointcut)具体要增强的连接点,通过表达式指定(如"增强com.niit包下所有Service的方法")。
  • 织入(Weaving):将切面的通知"织入"到目标方法的过程(Spring在运行时完成织入)。

三、AOP在Spring中的应用

Spring对AOP提供了完善的支持,我们可以通过XML配置或注解实现AOP。下面结合代码,用XML配置方式实战Spring AOP。

3.1 准备依赖

使用Spring AOP需添加aspectjweaver依赖:

xml 复制代码
<!-- AspectJ织入依赖:支持AOP功能的核心依赖 -->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.9.1</version>
</dependency>

3.2 配置Spring的AOP命名空间

applicationContext.xml中,添加AOP的命名空间和约束:

xml 复制代码
<?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">

        <!-- 组件扫描:让Spring管理带@Service/@Component等注解的类 -->
       <context:component-scan base-package="org.example.aop1"/>
</beans>

3.3 定义业务接口与实现类

定义接口和实现类:

学生服务接口(StudentService.java)

java 复制代码
public interface StudentService {
    void add();   // 添加学生
    void del();   // 删除学生
    void update();// 修改学生
    void query(); // 查询学生
}

学生服务实现类(StudentServiceImpl.java)

java 复制代码
import org.springframework.stereotype.Component;

// 注册为Spring的Bean,id为"ssi"
@Component("ssi")
public class StudentServiceImpl implements StudentService {
    @Override
    public void add() {
        System.out.println("【核心业务】添加学生");
    }

    @Override
    public void del() {
        System.out.println("【核心业务】删除学生");
    }

    @Override
    public void update() {
        System.out.println("【核心业务】修改学生");
    }

    @Override
    public void query() {
        System.out.println("【核心业务】查询学生");
    }
}

3.4 定义"切面"类(增强逻辑)

创建"日志切面",在方法执行前打印增强信息:

java 复制代码
import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

// 注册为Spring的Bean
@Component
public class LogBefore implements MethodBeforeAdvice {
    /**
     * 方法执行前的增强逻辑
     * @param method 被增强的方法
     * @param args 方法参数
     * @param target 被增强的目标对象
     */
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("【AOP增强】" + 
                           "类:" + target.getClass().getName() + 
                           ",方法:" + method.getName() + 
                           " 即将执行~");
    }
}

3.5 配置AOP:指定切入点与通知

applicationContext.xml中,配置"哪些方法"要被"哪个切面"增强:

xml 复制代码
<!-- AOP配置:将增强逻辑织入目标方法 -->
<aop:config>
    <!-- 1. 定义切入点:通过表达式指定要增强的方法 -->
    <!-- execution(* com.niit.aop1.*.*(..)) 含义:
         - 第一个*:返回值类型任意
         - com.niit.aop1.*:com.niit.aop1包下的任意类
         - 第二个*:类中的任意方法
         - (..):方法参数任意(个数、类型不限)
    -->
    <aop:pointcut id="myPointcut" expression="execution(* org.example.aop1(..))"/>
    
    <!-- 2. 配置通知:将LogBefore的增强逻辑,织入到myPointcut指定的方法中 -->
    <aop:advisor advice-ref="logBefore" pointcut-ref="myPointcut"/>
</aop:config>

配置说明:

  • aop:pointcut:通过expression表达式定义切入点(要增强的方法)。
  • aop:advisor:将通知advice-ref指定的LogBefore)与切入点pointcut-ref指定的myPointcut)关联,完成"织入"。

四、测试Spring AOP

编写测试类,获取StudentService的Bean并调用方法,观察AOP是否生效:

java 复制代码
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    public static void main(String[] args) {
        // 加载Spring配置文件
        ClassPathXmlApplicationContext context = 
                new ClassPathXmlApplicationContext("applicationContext.xml");
        // 获取StudentService的Bean(注意:此时获取的是Spring生成的代理对象)
        StudentService studentService = (StudentService) context.getBean("ssi");
        // 调用方法,触发AOP增强
        studentService.add();
        studentService.query();
        // 关闭容器
        context.close();
    }
}

运行结果:

可以看到:无需修改StudentServiceImpl的代码,方法执行前自动添加了日志增强逻辑------这就是AOP的"无侵入式增强"魅力。


我的个人主页,欢迎来阅读我的其他文章
https://blog.csdn.net/2402_83322742?spm=1011.2415.3001.5343

我的Java-Spring入门指南知识文章专栏
欢迎来阅读指出不足
https://blog.csdn.net/2402_83322742/category_13040333.html?spm=1001.2014.3001.5482

|--------------------|
| 非常感谢您的阅读,喜欢的话记得三连哦 |

相关推荐
吴声子夜歌14 分钟前
正则指引——匹配原理
java·正则表达式
未秃头的程序猿1 小时前
凌晨3点被叫醒:线上OOM,我用这套流程40分钟定位根因
java·jvm·后端
letisgo51 小时前
JDK21 + Spring AI 2.0 入场指南:2026 年 Java 开发者 AI 架构全景图
java·ai·idea·jdk21
白仑色1 小时前
Spring Boot 从切面统一控制事务
java·spring boot·aop·spring事务
Flynt1 小时前
Java并行流,让我debug了一整天
java
DFT计算杂谈1 小时前
FeSe超薄膜在CaF2衬底上的电子结构DFT研究
java·服务器·前端
用户3126874877201 小时前
Spring Boot 异常处理到底怎么玩的?从 DispatcherServlet 到全局兜底的全链路拆解
spring
circuitsosk2 小时前
长文本与高并发下的Token“瘦身”策略:Prompt压缩与上下文窗口优化
java·前端·python·prompt·上下文窗口·token优化
岁岁养乐多2 小时前
LangChain4j 工厂模式
java·开发语言
小小小米粒2 小时前
idea常用搭配
java