一、AOP的使用
在 Spring AOP 中,使用 @Around 注解时,你可以通过控制 ProceedingJoinPoint 的执行时机来在方法执行前和执行后分别插入逻辑。
1. 在目标方法执行前插入逻辑
在调用 joinPoint.proceed() 之前编写代码,所有逻辑会在目标方法执行之前执行。
2. 在目标方法执行后插入逻辑
在 joinPoint.proceed() 调用之后编写代码,所有逻辑会在目标方法执行之后执行。
注意:
①调用joinPoint.proceed() ,切面类的代码会阻塞住
②一般切面类返回的是joinPoint.proceed()的返回值
@Aspect
@Component
public class LoggingAspect {
@Around("execution(* com.example.service.*.*(..))")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
// 目标方法执行前的操作
System.out.println("Before method execution: " + joinPoint.getSignature().getName());
// 调用目标方法
Object result = joinPoint.proceed(); // 这行代码执行目标方法
// 目标方法执行后的操作
System.out.println("After method execution: " + joinPoint.getSignature().getName());
return result;
}
}
二、配合AOP用于登录判断
1、创建自定义注解
我们先定义一个自定义注解,在这里的作用是一个标识,真正的逻辑代码在切面类中
/**
* 自定义登录注解
*/
//登录判断
@Target(ElementType.METHOD) //用于加载方法上面
@Retention(RetentionPolicy.RUNTIME)//运行时执行
public @interface GuiguLogin {
}
2、创建切面类
使用环绕
@Around("execution(* com.atguigu.daijia.*.controller.*.*(..)) " + "&& @annotation(guiguLogin)")
/**
* 创建切面类
*/
@Component
@Aspect
public class GuiguLoginAspect {
@Autowired
RedisTemplate redisTemplate;
/**
* 在切入点执行的方法 --这个方法判断用户有没有登录(根据请求头中的token)
* @param proceedingJoinPoint
* @param guiguLogin
* @return
* @throws Throwable
*/
//在com.atguigu.daijia.*.controller包下的所有类的所有方法及含有guiguLogin注解进行增强
@Around("execution(* com.atguigu.daijia.*.controller.*.*(..)) " +
"&& @annotation(guiguLogin)")
public Object login(ProceedingJoinPoint proceedingJoinPoint,
GuiguLogin guiguLogin) throws Throwable {
//1、获取请求头
RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes sra = (ServletRequestAttributes)attributes;
HttpServletRequest request = sra.getRequest();
//2、从请求头中获取token
String token = request.getHeader("token");
//3、非空判断
if(!StringUtils.hasText(token)){
//请求头中没有token,未登录
throw new GuiguException(ResultCodeEnum.LOGIN_AUTH);
}
//4、根据token从redis中查看用户信息
String customerId
= (String) redisTemplate.opsForValue().get(RedisConstant.USER_LOGIN_KEY_PREFIX + token);
if(StringUtils.hasText(customerId)){
//5、将用户信息放入ThreadLocal
AuthContextHolder.setUserId(Long.parseLong(customerId));
}
//todo 以后看看要不要做非空判断,如果是空就抛出错误
return proceedingJoinPoint.proceed();//执行业务方法
}
}
在需要验证登录的代码的地方加上这个自定义注解
注意:
自定义注解配合AOP:适合需要灵活、声明式的方式来管理验证逻辑和切面功能的场景,尤其是当你希望将验证逻辑与业务逻辑分离时。适合复杂的验证需求和统一管理。
拦截器:适合简单、直观的请求处理和验证,特别是当你需要明确控制请求处理的前后顺序时。适合简单场景或者不需要复杂功能扩展的情况。