Spring Aop理解

AOP是一种编程范式,主要用于增强代码的可维护性和复用性,通过横切关注点来解耦业务逻辑和通用功能(如日志、权限验证、事务管理等)。


AOP 的核心概念

  1. 切面(Aspect):封装通用功能(如日志、事务、权限等)的模块。
  2. 通知(Advice):切面中定义的具体操作,如方法执行前后插入代码。
  3. 切点(Pointcut):定义在哪些地方插入通知,比如对某些类或方法进行拦截。
  4. 目标对象(Target):被 AOP 代理的业务类。
  5. 代理(Proxy):AOP 通过动态代理机制增强目标对象的方法执行。
  6. 织入(Weaving):将切面应用到目标对象的过程,Spring 使用动态代理实现。

举个例子:用 AOP 记录日志

1. 传统方式(不使用 AOP)

如果我们需要在多个方法中添加日志记录代码,代码会变得冗余难以维护

public class OrderService {
    public void createOrder() {
        System.out.println("日志:创建订单");
        System.out.println("订单创建成功");
    }

    public void cancelOrder() {
        System.out.println("日志:取消订单");
        System.out.println("订单取消成功");
    }
}

如果未来需要修改日志逻辑,就要修改所有方法,这显然不符合开闭原则(OCP)


2. 使用 Spring AOP

通过 AOP,我们可以把日志代码从业务逻辑中剥离出来,让 OrderService 只关注业务逻辑,而日志由切面自动处理。

(1)引入 AOP 依赖(Spring Boot 项目)

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

(2)定义业务类

import org.springframework.stereotype.Service;

@Service
public class OrderService {
    public void createOrder() {
        System.out.println("订单创建成功");
    }

    public void cancelOrder() {
        System.out.println("订单取消成功");
    }
}

(3)定义切面(Aspect)

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class LoggingAspect {
    
    @Before("execution(* com.example.service.OrderService.*(..))")  // 切点:拦截 OrderService 的所有方法
    public void logBeforeMethod() {
        System.out.println("日志:方法执行前记录日志");
    }
}

(4)测试效果

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class AopExampleApplication implements CommandLineRunner {

    @Autowired
    private OrderService orderService;

    public static void main(String[] args) {
        SpringApplication.run(AopExampleApplication.class, args);
    }

    @Override
    public void run(String... args) {
        orderService.createOrder();
        orderService.cancelOrder();
    }
}

(5)输出结果

日志:方法执行前记录日志
订单创建成功
日志:方法执行前记录日志
订单取消成功

AOP 例子:权限校验

在实际开发中,很多方法需要进行权限校验,比如用户是否登录、是否有某个角色等。
如果我们在每个业务方法中手动添加权限检查代码,会导致代码冗余、可维护性差
AOP 允许我们将权限校验逻辑从业务代码中解耦,提升代码的整洁度和复用性。


传统方式(未使用 AOP)

假设 UserService 需要检查用户是否有权限:

public class UserService {
    public void getUserInfo(String username) {
        if (!checkPermission()) {
            throw new RuntimeException("无权限访问");
        }
        System.out.println("查询用户信息:" + username);
    }

    private boolean checkPermission() {
        // 假设这里是权限检查逻辑
        return Math.random() > 0.5;  // 50% 概率通过
    }
}

问题:

  • 每个方法都需要写 checkPermission(),代码冗余。
  • 如果权限逻辑修改,需要修改多个地方。

使用 AOP 实现权限校验

(1)定义业务类

import org.springframework.stereotype.Service;

@Service
public class UserService {
    public void getUserInfo(String username) {
        System.out.println("查询用户信息:" + username);
    }

    public void deleteUser(String username) {
        System.out.println("删除用户:" + username);
    }
}

此时业务方法不关心权限问题,AOP 会自动处理。


(2)定义权限切面

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class AuthAspect {

    @Around("execution(* com.example.service.UserService.*(..))") // 拦截 UserService 所有方法
    public Object checkPermission(ProceedingJoinPoint joinPoint) throws Throwable {
        if (!hasPermission()) {
            throw new RuntimeException("无权限访问:" + joinPoint.getSignature().getName());
        }
        System.out.println("权限校验通过:" + joinPoint.getSignature().getName());
        return joinPoint.proceed(); // 继续执行目标方法
    }

    private boolean hasPermission() {
        // 模拟权限校验(50% 通过)
        return Math.random() > 0.5;
    }
}

核心逻辑:

  • @Around 注解:拦截 UserService 的所有方法。
  • hasPermission():模拟权限校验,50% 通过。
  • joinPoint.proceed():权限通过后执行原方法,否则抛异常。

(3)测试效果

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class AopAuthApplication implements CommandLineRunner {

    @Autowired
    private UserService userService;

    public static void main(String[] args) {
        SpringApplication.run(AopAuthApplication.class, args);
    }

    @Override
    public void run(String... args) {
        try {
            userService.getUserInfo("Alice");
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }

        try {
            userService.deleteUser("Bob");
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }
}

(4)运行结果

可能的输出:

无权限访问:getUserInfo
权限校验通过:deleteUser
删除用户:Bob

或者

权限校验通过:getUserInfo
查询用户信息:Alice
无权限访问:deleteUser

解释:

  • 由于 hasPermission() 是随机的,可能某个方法被拦截,也可能通过。
  • 如果拦截,AOP 直接抛出 "无权限访问" 异常,业务方法不会执行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值