SpringBoot切面编程
众所周知,spring最核心的两个功能是aop和ioc,即面向切面和控制反转。本文会讲一讲SpringBoot如何使用AOP实现面向切面的过程原理。
何为AOP
AOP(Aspect OrientedProgramming):面向切面编程,面向切面编程(也叫面向方面编程),是目前软件开发中的一个热点,也是Spring框架中的一个重要内容。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
利用AOP可以对我们边缘业务进行隔离,降低无关业务逻辑耦合性。提高程序的可重用性,同时提高了开发的效率。一般用于日志记录,性能统计,安全控制,权限管理,事务处理,异常处理,资源池管理
。使用场景
AOP的一些核心概念
概念 | 定义 | 作用 | 示例 |
---|---|---|---|
横切关注点 | 多个模块中共同涉及的功能,如日志、事务、权限等 | 分离通用功能,避免代码冗余 | 所有Service方法都需要记录执行时间 |
切面(Aspect) | 封装横切关注点的模块,包含切入点和通知 | 将横切逻辑集中管理 | 定义一个日志切面,统一处理日志记录 |
连接点(Join Point) | 程序执行中的特定点,如方法调用、字段修改等 | 作为切面织入的候选位置 | 某个Service方法被调用时 |
切入点(Pointcut) | 定义切面作用的具体位置,通过表达式匹配连接点 | 精确控制切面影响的范围 | 匹配所有以save 开头的方法 |
通知(Advice) | 切面在连接点执行的操作,分为前置、后置、返回、异常、环绕通知 | 实现具体的横切功能 | 在方法执行前记录参数,执行后记录返回值 |
织入(Weaving) | 将切面与目标对象连接并创建代理对象的过程 | 使切面逻辑在特定时机生效 | 编译时织入(AspectJ)、运行时织入(Spring AOP) |
目标对象(Target) | 被切面包裹的对象 | 被增强的原始业务对象 | 实际的UserService类 |
代理对象(Proxy) | 织入切面后生成的对象 | 替代原始对象,执行时包含切面逻辑 | 通过JDK动态代理生成的UserService代理对象 |
代码实现
这段代码展示了AOP(面向切面编程)在权限校验场景中的典型应用。通过自定义注解和环绕通知,实现了方法级别的权限拦截,避免了在每个业务方法中重复编写权限校验逻辑。
1. 自定义注解 @AuthCheck
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface AuthCheck {String mustRole() default "";
}
- 作用:标记需要进行权限校验的方法,并指定所需的角色(如
@AuthCheck(mustRole = "ADMIN")
)。 - 元注解:
@Target(ElementType.METHOD)
:注解仅可用于方法。@Retention(RetentionPolicy.RUNTIME)
:注解在运行时保留,以便反射获取。
2. 切面类 AuthInterceptor
@Aspect
@Component
public class AuthInterceptor {@Resourceprivate UserService userService;@Around("@annotation(authCheck)")public Object doInterceptor(ProceedingJoinPoint joinPoint, AuthCheck authCheck) throws Throwable {// 1. 获取注解中指定的必须角色String mustRole = authCheck.mustRole();// 2. 获取当前登录用户HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();User loginUser = userService.getLoginUser(request);// 3. 权限校验逻辑UserRoleEnum mustRoleEnum = UserRoleEnum.getEnumByValue(mustRole);if (mustRoleEnum == null) return joinPoint.proceed(); // 无需权限,放行UserRoleEnum userRoleEnum = UserRoleEnum.getEnumByValue(loginUser.getUserRole());if (userRoleEnum == null) throw new BusinessException(ErrorCode.NO_AUTH_ERROR);// 管理员权限校验if (mustRoleEnum == UserRoleEnum.ADMIN && userRoleEnum != UserRoleEnum.ADMIN) {throw new BusinessException(ErrorCode.NO_AUTH_ERROR);}// 校验通过,执行原方法return joinPoint.proceed();}
}
- 关键元素:
@Aspect
:声明该类为切面。@Component
:将切面注册为Spring Bean。@Around("@annotation(authCheck)")
:- 环绕通知,拦截所有标记了
@AuthCheck
注解的方法。 authCheck
参数绑定当前方法上的@AuthCheck
注解实例。
- 环绕通知,拦截所有标记了
AOP思想的体现
1. 关注点分离
- 业务逻辑(如用户服务)与权限校验完全解耦。
- 权限校验逻辑集中在切面中,无需在每个业务方法中重复编写。
2. 声明式编程
- 通过
@AuthCheck
注解在方法上声明所需权限,简洁直观。 - 示例:
@AuthCheck(mustRole = "ADMIN") public void deleteUser(Long userId) {// 业务逻辑(无需关心权限校验) }
3. 动态代理机制
- Spring AOP通过动态代理(JDK或CGLIB)在运行时生成代理对象。
- 代理对象在调用目标方法前后插入权限校验逻辑:
调用代理方法 → 执行前置校验 → 执行目标方法 → 执行后置逻辑
执行流程
- 方法调用:客户端调用标记了
@AuthCheck
的方法。 - 代理拦截:Spring AOP拦截调用,执行
AuthInterceptor
的环绕通知。 - 权限校验:
- 从注解获取所需角色(如
ADMIN
)。 - 从当前请求获取登录用户信息。
- 比较用户角色与注解要求的角色。
- 从注解获取所需角色(如
- 结果处理:
- 校验通过:执行原方法。
- 校验失败:抛出
BusinessException
。