金昌市网站建设_网站建设公司_门户网站_seo优化
2026/1/10 3:25:52 网站建设 项目流程

目录

1.什么是AOP?

1.1基本概念

1.2具体应用

2.AOP是怎么怎么实现的?

2.1静态代理

2.2动态代理

2.2.1cglib 动态代理

2.2.2 JDK 动态代理

3.AOP中的核心概念

4.AOP具体实现(权限校验)

1.详细版

2.精简版

5总结


大家好!今天咱们来聊聊Spring框架中一个非常核心且实用的特性——AOP。很多小伙伴刚接触AOP时会觉得抽象,比如“什么是切面?”“动态代理到底是啥?”“实际开发中我该怎么用?”。这篇文章就从基础概念核心原理,再到实际应用,一步步把Spring AOP讲明白,小白也能轻松看懂~,发车咯~

使用 Spring 的面向切面编程 :: Spring 框架 - Spring 框架(官网地址)

1.什么是AOP?

1.1基本概念

英文名叫Aspect-Oriented Programming,就是面向切面编程嘛!,将冗余代码抽离出来,组成一个切面,想把刀子一样插入到原来的代码中,是为了让我们尽量少的写代码的。有个叫AspectJ的框架的东西,可以将属性、方法、代码块批量增强。AOP就借鉴到了AspecJ框架。

1.2具体应用

在许多情况下,我们需要对用户操作进行权限校验,因为在有的场景下只有系统中管理员才有权限操作的,我们就假设在我们的系统中新增用户更新用户需要管理员权限。

在下图中新增用户更新用户这两个方法里,都重复写了 “用户权限校验” 的代码 —— 如果有 10 个类似的方法,就要写 10 遍权限校验,既冗余又难维护。

这时天使宝宝AOP来了, 把 “用户权限校验” 抽成一个独立的切面,通过 AOP 动态 “切入” 到新增、更新方法的执行流程中。

总结一下:AOP将那些与业务逻辑无关,但多个业务模块都需要的公共功能(比如日志、权限、事务),抽取出来单独维护,然后在不修改原有业务代码的前提下,动态地切入到业务方法的指定位置

简单说,AOP就是“抽离公共代码,动态植入业务”,实现代码解耦复用

2.AOP是怎么怎么实现的?

AOP是通过代理来实现的,想象成一个小助理可以代理我们的类执行对应的代码逻辑。代理也分为静态代理动态代理两种。

2.1静态代理

静态代理,静态代理是通过代码运行前,通过修改class文件来完成代理,功能强大,较为复杂,运行效率比动态代理块。静态代理分为,编译前,编译后,加载时三种。这三种作为了解即可

  • 编译前:在代码编写完成后、编译为.class 文件前,手动编写代理类并关联目标类,代理逻辑与目标逻辑在编码阶段已绑定。
  • 编译后:通过字节码修改已编译生成的.class 文件,将代理逻辑织入目标类的字节码中。
  • 加载时:在类加载器将.class 文件加载到JVM的过程中,通过自定义类加载器拦截加载流程,动态修改类字节码以植入代理逻辑。

2.2动态代理

动态代理分为两种分为cglibjdk两种 Spring 框架会根据目标类是否实现接口,自动选择Jdkcglib动态代理

2.2.1cglib 动态代理

  • 基于字节码生成目标类的子类,可代理未实现接口的类;
  • 依赖第三方库(需引入 cglib 包),但不支持代理 final 类 / 方法。

2.2.2 JDK 动态代理

  • 基于接口实现,仅能代理实现了接口的类;
  • 利用 Java反射机制生成代理对象,无需额外依赖。

3.AOP中的核心概念

这些概念先在脑子里留个印象就信,我们等下将一些概念实际应用~~~

1.切面(Aspect):抽离出来的公共功能模块(比如“日志切面”“权限切面”)。一个切面里包含了“要做什么”(通知)和“在哪里做”(切入点)。

2.通知(Advice):切面里具体要执行的逻辑(比如“记录接口入参”“记录接口耗时”)。Spring提供了5种通知类型,对应不同的切入时机:

    • 前置通知(Before):业务方法执行前执行(比如记录请求开始时间)
    • 后置通知(After):业务方法执行后执行(无论成功还是失败,比如记录请求结束)
    • 返回通知(AfterReturning):业务方法成功执行后执行(比如记录接口返回结果)
    • 异常通知(AfterThrowing):业务方法抛出异常时执行(比如记录异常信息)
    • 环绕通知(Around):包裹业务方法,在方法执行前后都能执行(功能最强,比如统计接口耗时)

3.切入点(Pointcut):定义“哪些业务方法需要被切入”。切入点通常用表达式来描述。

4.连接点(JoinPoint):程序运行中可能被切入的(比如方法执行前、执行后、抛出异常时)。每个切入点对应的都是多个连接点。

5.目标对象(Target):被切入的业务对象(比如咱们的UserController、OrderService)。

6.代理对象(Proxy):Spring AOP通过动态代理技术,为目标对象创建的代理对象。实际调用时,咱们调用的是代理对象,代理对象会先执行切面逻辑,再执行目标对象的业务方法。

7.织入(Weaving):将切面逻辑动态植入到目标对象方法中的过程(Spring在运行时完成织入)。

其中,通知(Advice)切面核心逻辑,Spring 提供了 5 种通知类型,对应不同的切入时机,实战中最常用的是环绕通知。

4.AOP具体实现(权限校验)

了解了 AOP 的原理和核心概念后,咱们来做一个企业级实战案例—— 用 AOP 实现接口的权限校验,这是项目中非常非常非常常用的功能。我们分为详细版精简版实现,可以选择食用!!!

实战需求

  • 定义一个权限校验注解 @AuthCheck,可指定方法所需的角色(如 admin、user);
  • 用 AOP 环绕通知拦截所有标注了 @AuthCheck 的方法;
  • 校验逻辑:未登录 → 抛未授权异常;已登录但角色不匹配 → 抛无权限异常;角色匹配 → 执行目标方法。

1.详细版

1.1 创建自定义权限注解 @AuthCheck

注解用于标记需要进行权限校验的方法,指定方法所需的角色。

import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 权限校验注解 * 用于标记需要进行角色权限校验的方法,仅作用于方法级别 */ @Target(ElementType.METHOD) // 注解仅能作用于方法上 @Retention(RetentionPolicy.RUNTIME) // 注解在运行时保留,可通过反射获取 public @interface AuthCheck { /** * 方法执行所需的必选角色标识 * 示例:"admin" 表示需要管理员角色,"user" 表示需要普通用户角色 * 默认值为空字符串,表示无需特定角色(仅需登录即可) * @return 角色标识字符串 */ String mustRole() default ""; }

1.2 创建自定义异常类

定义未授权(未登录)和无权限(角色不匹配)的自定义异常,用于权限校验失败时抛出。

/** * 未授权异常(对应HTTP 401状态码) * 用于用户未登录时抛出 */ public class UnauthorizedException extends RuntimeException { public UnauthorizedException(String message) { super(message); } } /** * 禁止访问异常(对应HTTP 403状态码) * 用于用户已登录但角色不匹配时抛出 */ public class ForbiddenException extends RuntimeException { public ForbiddenException(String message) { super(message); } }

1.3 创建角色枚举和常量(可选)

为了规范角色标识,避免硬编码,可创建角色枚举或常量类。

/** * 角色枚举类 */ public enum UserRoleEnum { ADMIN("admin", "管理员"), USER("user", "普通用户"); private final String value; private final String desc; UserRoleEnum(String value, String desc) { this.value = value; this.desc = desc; } public String getValue() { return value; } public String getDesc() { return desc; } } /** * 角色常量类(也可直接使用枚举) */ public class UserConstant { public static final String ADMIN_ROLE = "admin"; public static final String USER_ROLE = "user"; }

1.4 实现 AOP 权限拦截器

创建切面类,使用环绕通知实现权限校验逻辑,核心流程:

  1. 从请求中获取当前登录用户;
  2. 校验用户是否已登录;
  3. 从 @AuthCheck 注解中获取所需角色;
  4. 校验用户角色是否匹配;
  5. 角色匹配则执行目标方法,不匹配则抛出异常。
import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; /** * 权限校验AOP拦截器 * 拦截所有标注@AuthCheck注解的方法,执行角色权限校验 */ @Aspect // 标识该类为AOP切面类 @Component // 交给Spring容器管理,使切面生效 public class AuthInterceptor { /** * 注入用户服务,用于获取当前登录用户信息 * 实际项目中,UserService需实现从请求中解析登录用户(如通过Token解析) */ @Resource private UserService userService; /** * 环绕通知:拦截所有标注@AuthCheck注解的方法 * @param joinPoint 切入点对象,可获取目标方法信息、执行目标方法 * @param authCheck 目标方法上的@AuthCheck注解实例,用于获取所需角色 * @return 目标方法的执行结果 * @throws Throwable 目标方法执行过程中抛出的异常 */ @Around("@annotation(authCheck)") // 切入点表达式:拦截所有标注@AuthCheck注解的方法 public Object doIntercept(ProceedingJoinPoint joinPoint, AuthCheck authCheck) throws Throwable { // 1. 获取注解配置的必选角色 String mustRole = authCheck.mustRole(); // 2. 获取当前HTTP请求对象(从请求上下文获取) ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes(); HttpServletRequest request = requestAttributes.getRequest(); // 3. 从请求中获取当前登录用户(实际项目中需通过Token、Session等方式解析) User loginUser = userService.getLoginUser(request); // 4. 基础校验:未登录则抛出未授权异常 if (loginUser == null) { throw new UnauthorizedException("未登录,无法执行该操作"); } // 5. 无指定必选角色:仅需登录即可,直接执行目标方法 if ("".equals(mustRole)) { return joinPoint.proceed(); // 执行目标方法 } // 6. 管理员角色校验:如果注解指定角色为admin,需校验用户是否为管理员 if (UserRoleEnum.ADMIN.getValue().equals(mustRole)) { if (!isAdmin(loginUser)) { throw new ForbiddenException("无管理员权限,禁止执行该操作"); } return joinPoint.proceed(); // 权限通过,执行目标方法 } // 7. 普通角色校验:当前用户角色需与注解指定角色完全匹配 String userRole = loginUser.getUserRole(); if (!mustRole.equals(userRole)) { throw new ForbiddenException(String.format("无[%s]角色权限,禁止执行该操作", mustRole)); } // 8. 所有校验通过,执行目标方法并返回结果 return joinPoint.proceed(); } /** * 辅助方法:判断当前登录用户是否为管理员 * @param user 登录用户对象 * @return true-管理员,false-非管理员 */ private boolean isAdmin(User user) { return user != null && UserRoleEnum.ADMIN.getValue().equals(user.getUserRole()); } }

1.5 在业务方法中使用注解

在需要进行权限校验的业务方法上标注 @AuthCheck 注解,并指定所需角色即可。

import org.springframework.stereotype.Service; /** * 用户业务服务类 */ @Service public class UserService { /** * 新增用户(仅管理员可执行) * 标注@AuthCheck,指定必须有admin角色 */ @AuthCheck(mustRole = UserRoleEnum.ADMIN.getValue()) public void addUser(User user) { // 核心业务逻辑:新增用户到数据库 userMapper.insert(user); } /** * 更新用户信息(普通用户可执行) * 标注@AuthCheck,指定必须有user角色 */ @AuthCheck(mustRole = UserRoleEnum.USER.getValue()) public void updateUser(User user) { // 核心业务逻辑:更新用户信息 userMapper.updateById(user); } /** * 查询用户信息(仅需登录,无特定角色要求) * 标注@AuthCheck,不指定mustRole(默认空字符串) */ @AuthCheck public User getUserById(Long userId) { // 核心业务逻辑:查询用户信息 return userMapper.selectById(userId); } // 其他方法... }

2.精简版

2.1编写权限校验注解创建一个接口

import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 权限校验注解 * 用于标记需要进行角色权限校验的方法,仅作用于方法级别 */ @Target(ElementType.METHOD) // 注解仅能作用于方法上 @Retention(RetentionPolicy.RUNTIME) // 注解在运行时保留,可通过反射获取 public @interface AuthCheck { /** * 方法执行所需的必选角色标识 * 示例:"admin" 表示需要管理员角色,"user" 表示需要普通用户角色 * 默认值为空字符串,表示无需特定角色(仅需登录即可) * @return 角色标识字符串 */ String mustRole() default ""; }

2.2编写校验AOP,采取环绕通知。

@Aspect @Component public class AuthInterceptor { @Resource private UserService userService; /** * 执行拦截 * * @param joinPoint 切入点 * @param authCheck 权限校验注解 */ @Around("@annotation(authChek)") //@annotation(...) 表示 “拦截所有被括号中指定注解标记的方法”。 public Object doIntercept(ProceedingJoinPoint joinPoint, AuthCheck authCheck) throws Throwable { //逻辑校验,若校验失败则抛出异常 // //代码省略........... // return joinPoint.proceed(); } }

2.3在想要校验的方法上添加注解

@AuthCheck(mustRole = UserConstant.ADMIN_ROLE)

5总结

Spring AOP是一种非常强大的编程思想,核心是通过代理将公共功能抽离切面,动态植入业务方法,实现解耦代码复用

核心要点回顾

  1. AOP 的核心思想:抽离公共代码,动态植入业务
  2. 底层实现:静态代理(AspectJ)和动态代理(JDK/CGLIB),Spring AOP默认使用动态代理
  3. 核心概念:切面、通知、切入点、连接点、目标对象、代理对象、织入
  4. 实战关键:通过自定义注解 + 环绕通知,可快速实现权限校验、日志记录等功能

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询