Spring Aop

#spring #AOP

1.AOP(Aspect-Oriented Programming) 面向切面编程

![](/pics/redis/Pasted image 20251020085030.png)

解耦代码 eg: 有多段类似业务代码,执行时需要进行log输出和判断

@Service
public class UserService {

	public void addUser(String name, int age) {

	System.out.println("add user: " + name + ", age: " + age);

	}

	public void deleteUser(int id) throws Exception {

	System.out.println("delete user: " + id);

	}
}

传统方法:

@Service
public class UserService {

	public void addUser(String name, int age) {
	System.out.println("[log] method addUser")
	System.out.println("add user: " + name + ", age: " + age);

	}

	public void deleteUser(int id) throws Exception {
	logBefore();
	System.out.println("delete user: " + id);
	logAfter();

	}
}

每段业务需要编写对应log输出或者是调用对应的日志输出方法,对业务代码侵入性强,出现了代码冗余.

AOP:

//UserService
@Service
public class UserService {

	public void addUser(String name, int age) {

	System.out.println("add user: " + name + ", age: " + age);

	}

	public void deleteUser(int id) throws Exception {

	System.out.println("delete user: " + id);

	}
}


//LogAspect
@Aspect  
@Component  
public class LogAspect {  
    @Pointcut(("within(@org.springframework.stereotype.Service *)"))  
    // @Around(execution(* com.example.service.*.*(..)))  
    public void logPoint() {}
	@Around("logPoint()")  
	public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {  
	    long start = System.currentTimeMillis();  
	    Object[] args = joinPoint.getArgs();  
	    System.out.println("[log] 方法Args:" + Arrays.toString(args));  
	    Object proceed = joinPoint.proceed();  
	    long end = System.currentTimeMillis();  
	    System.out.println("[log] 耗时:" + (end - start) + "ms");  
	    return proceed;  
	}
}

AOP仅需要编写好对应的切面代码,即可监听控制所需要的代码

2. AOP核心概念

概念 解释
Aspect(切面) 切断业务逻辑后进入的模块
Pointcut(切入点) 监听代码切断代码的表达式
JoinPoint(连接点) 被暂停方法处的上下文信息
Advice(通知) 在JoinPoint所执行的操作
Weaving(织入) 将Aspect应用到代码的过程

1. PointCut

@Pointcut(表达式)

  • @Pointcut(("within(@org.springframework.stereotype.Service *)")) 拦截所有@Service的Bean

  • @Pointcut("@annotation(com.example.springBean.RequireAdmin)") 拦截springBean下RequireAdmin方法

eg:

@Pointcut(("within(@org.springframework.stereotype.Service *)"))
public void logPoint() {}

@Pointcut("execution(* com.example.springBean.UserService.*(..))")
public void ExceptionAspectLogPoint() {}

@Pointcut("@annotation(com.example.springBean.RequireAdmin)")
public void SecurityAspect() {}

创建切入点后进行重命名,可以简化后续Advice的使用

2. JoinPoint

  • JoinPoint JoinPoint不能用在@Around, @Around需要使用 ProceedingJoinPoint
	@Before("logPoint()") 
	public void lMethod(JoinPoint joinPoint) {
		// 获取方法名 String 
		methodName = joinPoint.getSignature().getName(); 
		// 获取参数 
		Object[] args = joinPoint.getArgs();
	}
方法 说明 返回类型
getSignature() 获取被调用方法的签名(包含方法名、返回类型等) Signature
getArgs() 获取方法调用时的参数数组 Object[]
getTarget() 获取目标对象(被代理的对象) Object
getThis() 获取代理对象 Object
getKind() 获取连接点的类型(如方法执行、异常处理等) String
  • ProceedingJoinPoint
@Around("logPoint")
public Object Method(ProceedingJoinPoint joinPoint) throws Throwable {
	boolean hasPermission = checkPermission();

    if (hasPermission) {
        // 有权限,执行目标方法
        return joinPoint.proceed();
    } else {
        // 无权限,不执行目标方法,返回默认值或抛出异常
        System.out.println("[Around] No permission, method skipped.");
        return null; // 或者 throw new AccessDeniedException();
    }
}

新增的关键方法

方法名 返回类型 说明
proceed() Object 执行目标方法,并返回方法的返回值
proceed(Object[] args) Object 执行目标方法,并可以修改传入的参数

3.Advice

Advice 类型 执行时机
@Before 方法执行前
@After 方法执行后
@AfterReturning 方法正常返回后
@AfterThrowing 方法抛出异常后
@Around 方法执行前后
  1. @Before @After 适用于简单的前后置逻辑
  2. @AfterReturning @AfterThrowing 处理返回值和异常
  3. @Around 完全控制,如是否执行 修改返回值 传递参数

方法执行时若匹配到PointCut的表达式,将JointPoint处的方法上下文信息存储,进入Aspect,执行Advice.

3.示例

package com.example.springBean;  
  
import java.util.Arrays;  
  
import org.aspectj.lang.JoinPoint;  
import org.aspectj.lang.ProceedingJoinPoint;  
import org.aspectj.lang.annotation.AfterThrowing;  
import org.aspectj.lang.annotation.Around;  
import org.aspectj.lang.annotation.Aspect;  
import org.aspectj.lang.annotation.Pointcut;  
import org.springframework.stereotype.Component;  
  
@Aspect  
@Component  
public class LogAspect {  
    @Pointcut(("within(@org.springframework.stereotype.Service *)"))  
    public void logPoint() {}  
  
    @Pointcut("execution(* com.example.springBean.UserService.*(..))")  
    public void ExceptionAspectLogPoint() {}  
  
    @Pointcut("@annotation(com.example.springBean.RequireAdmin)")  
    public void SecurityAspect() {}  
  
    @Around("logPoint()")  
    public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {  
        long start = System.currentTimeMillis();  
        Object[] args = joinPoint.getArgs();  
        System.out.println("[log] 方法Args:" + Arrays.toString(args));  
        Object proceed = joinPoint.proceed();  
        long end = System.currentTimeMillis();  
        System.out.println("[log] 耗时:" + (end - start) + "ms");  
        return proceed;  
  
    }  
  
    @AfterThrowing(pointcut = "ExceptionAspectLogPoint()", throwing = "ex")  
    public void logException(JoinPoint JoinPoint, Exception ex) {  
        String methodName = JoinPoint.getSignature().getName();  
        System.out.println("[ExceptionLog] Method:" + methodName + " 异常信息 :" + ex.getMessage());  
    }  
  
    @Around("SecurityAspect()")  
    public Object checkSecurity(ProceedingJoinPoint joinPoint) throws Throwable {  
        String methodName = joinPoint.getSignature().getName();  
        if (UserService.currentUser.equals("admin")) {  
            System.out.println("[SecurityLog] 当前用户有权限执行方法:" + methodName);  
            return joinPoint.proceed();  
        } else {  
            System.out.println("[SecurityLog] 当前用户无权限执行方法:" + methodName);  
            throw new SecurityException("当前用户无权限执行方法:" + methodName);  
        }  
    }  
  
  
  
}
package com.example.springBean;  
  
import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.stereotype.Service;  
  
@Service  
public class UserService {  
  
    public static String currentUser = "admin";  
  
    @RequireAdmin  
    public void addUser(String name, int age) {  
        System.out.println("add user: " + name + ", age: " + age);  
    }  
  
    public void deleteUser(int id) throws Exception {  
        if (id <= 0) {  
            throw new Exception("Invalid user ID");  
        }  
        System.out.println("delete user: " + id);  
    }  
}
package com.example.springBean;  
  
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 RequireAdmin {  
    String value() default "admin";  
}