一、面向切面的作用
1、解耦
面向切面编程将把切面代码脱离业务进行编码,降低了代码耦合性和维护成本。
2、面向切面使用思路
当部分业务接口需要登陆的情况下才能使用,这时候就使用面向切面编程,而不是在每个接口中进行判断用户是否登录。
日志添加。开发中常见日志的记录,这时候使用面向切面思想进行编码将减少在每个方法中实现日志。
二、面向切面实现
首先得先了解@Aspect注解。
@Aspect
是 Spring AOP(Aspect-Oriented Programming,面向切面编程)中的一个关键注解。AOP 是一种编程范式,它允许程序员定义横切关注点(cross-cutting concerns),这些关注点会“切入”到应用的多个不同点中,比如日志、事务管理等。
当你使用 Spring AOP 时,你会定义一个或多个切面(Aspect),这些切面包含了多个通知(Advice),这些通知定义了何时以及如何执行横切关注点。
@Aspect
注解用于声明一个类为切面类。这个类通常会包含多个通知方法,这些方法使用如 @Before
、@After
、@AfterReturning
、@AfterThrowing
和 @Around
等注解来指定它们的执行时机。
以下是一个简单的例子,展示如何使用 @Aspect
:
javaimport 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.myapp.MyClass.myMethod(..))")
public void logBefore() {
System.out.println("Before method execution");
}
}
在这个例子中:
@Aspect
注解表示这个类是一个切面类。@Component
注解让 Spring 知道这个类是一个 bean,需要被 Spring 容器管理。@Before
注解定义了一个前置通知(Before Advice),它会在com.example.myapp.MyClass.myMethod
方法执行之前被调用。
需要注意的是,要使 Spring AOP 工作,你还需要在 Spring 配置中启用它,通常是通过在配置类上添加 @EnableAspectJAutoProxy
注解来完成的。
此外,Spring AOP 默认是基于代理的,这意味着它只适用于单例 beans。如果你需要对原型(prototype)范围的 beans 使用 AOP,你可能需要考虑使用 AspectJ,它是一个更强大、更灵活的 AOP 实现。
execution
表达式的基本语法是:
javaexecution([权限修饰符] [返回类型] [方法名]([参数列表]))
这里是一些execution
表达式的例子:
-
匹配所有方法执行:
java
execution(* *(..))
-
匹配com.example包下所有类的所有方法:
java
execution(* com.example.*.*(..))
-
匹配com.example包下所有类的public方法:
java
execution(public * com.example.*.*(..))
-
匹配com.example.service包下所有类的返回类型为void的方法:
java
execution(void com.example.service.*.*(..))
-
匹配com.example.service.UserService类的所有方法:
java
execution(* com.example.service.UserService.*(..))
-
匹配com.example.service.UserService类的特定方法
findUserById
:java
execution(* com.example.service.UserService.findUserById(..))
你可以在切面类中使用@Pointcut
注解来定义一个切入点,然后在通知方法中使用这个切入点:
java@Aspect
@Component
public class LoggingAspect {
@Pointcut("execution(* com.example.service.*.*(..))")
public void serviceMethods() {}
@Before("serviceMethods()")
public void logBefore() {
System.out.println("Before method execution");
}
}
在这个例子中,serviceMethods
切入点匹配了com.example.service
包下所有类的所有方法。然后,@Before
注解使用了这个切入点,意味着logBefore
方法会在com.example.service
包下所有方法执行之前被调用。
为了使Spring AOP生效,你还需要在Spring配置中启用AOP支持,通常是通过在配置类上添加@EnableAspectJAutoProxy
注解来完成的。
三、ProceedingJoinPoint介绍
在Spring AOP(Aspect-Oriented Programming)中,ProceedingJoinPoint
是一个特殊的连接点(JoinPoint),它代表一个正在被执行的方法调用,并且允许你在该方法调用之前、之后或者替换该方法的执行。ProceedingJoinPoint
是JoinPoint
的子接口,它扩展了JoinPoint
的功能,允许你控制方法的执行流程。
当你定义一个环绕通知(Around Advice)时,你可以使用ProceedingJoinPoint
作为通知方法的参数。这样,你就可以在通知方法内部决定是否执行目标方法,何时执行,以及如何处理目标方法的返回值或异常。
以下是一个使用ProceedingJoinPoint
的环绕通知的例子:
javaimport 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 LoggingAspect {
@Around("execution(* com.example.myapp.MyClass.myMethod(..))")
public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
Object proceed = joinPoint.proceed(); // 执行目标方法
long executionTime = System.currentTimeMillis() - start;
System.out.println(joinPoint.getSignature() + " executed in " + executionTime + "ms");
return proceed;
}
}
在这个例子中:
@Around
注解定义了一个环绕通知,它匹配com.example.myapp.MyClass.myMethod
方法的执行。ProceedingJoinPoint
作为logExecutionTime
方法的参数,允许你在方法执行前后进行自定义操作。joinPoint.proceed()
调用实际执行了目标方法。- 在调用
proceed()
方法之前和之后,你可以添加自定义的逻辑,比如记录开始时间、计算执行时间等。
注意,使用ProceedingJoinPoint
时,你不能简单地调用目标方法,而是需要通过proceed()
方法。如果你直接调用目标方法,那么环绕通知将不会起作用,因为它会绕过AOP的代理机制。
另外,为了使Spring AOP工作,你需要在Spring配置中启用AOP支持,通常是通过在配置类上添加@EnableAspectJAutoProxy
注解来完成的。同时,确保你的切面类(在这个例子中是LoggingAspect
)被Spring容器管理,通常是通过添加@Component
注解来完成的。