一、名词解释
1.定义
1.1 Spring AOP(面向切面编程)是 Spring 框架中的一个关键特性,它允许你在应用程序的不同模块中横向地横切关注点(cross-cutting concerns)。这意味着你可以将代码逻辑从主要业务逻辑中分离出来,比如日志记录、性能监控、安全性等方面。Spring AOP 基于代理模式实现,它通过在运行时动态地创建代理对象来实现切面功能。
2.核心概念点
Spring AOP 主要基于以下几个核心概念:
1. 切面(Aspect):切面是一个横切关注点的模块化实现。在 Spring AOP 中,切面可以是一个类,其中包含了一系列的通知(advice)和切点(pointcut)。
2. 连接点(Join Point):连接点是在应用执行过程中可以被拦截到的点,比如方法调用或异常抛出事件等。
3. 通知(Advice):通知是切面在特定连接点执行的动作。在 Spring AOP 中,通知可以在连接点之前、之后或者环绕执行。
4. 切点(Pointcut):切点定义了在何处应用通知。它是一个表达式,匹配连接点的集合。
3.通知类型
Spring AOP 支持以下几种类型的通知:
- 前置通知(Before Advice):在连接点之前执行的通知。
- 后置通知(After Advice):在连接点之后执行的通知,不论连接点执行成功与否。
- 返回通知(After Returning Advice):在连接点正常完成后执行的通知,可以访问连接点的返回值。
- 异常通知(After Throwing Advice):在连接点抛出异常后执行的通知。
- 环绕通知(Around Advice):在连接点之前和之后执行的通知,它可以控制连接点的执行,包括是否执行连接点以及修改连接点的返回值。
Spring AOP 使用 XML 配置文件或者基于注解的方式来声明切面和通知。XML 配置方式相对传统,而基于注解的方式更加简洁和直观。使用 Spring AOP 可以帮助你将应用程序的核心业务逻辑与横切关注点(如日志、事务、安全等)分离开来,提高了代码的可维护性和灵活性。
二、代码实现
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.SourceLocation;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
@Aspect //AOP 必须注解
@Component //声明Bean
public class LogAopAspect {
/**
* execution( modifier? ret-type declaring-type?name-pattern(param-pattern) throws-pattern?) 用于匹配方法执行的连接点
* modifier 匹配修饰符 public private 省略时匹配任意修饰符
* ret-type 匹配返回类型 使用* 匹配
* declaring-type 匹配目标类 省略时匹配任意类型 .. 匹配包及其子包的所有类
* name-pattern 匹配方法名 使用* 表示通配符 * 表示任意 set* 匹配名称以set开头的方法
* param-pattern 匹配参数类型和数量 ()匹配没有参数的方法
* throws-pattern 匹配抛出异常类型 省略时匹配任意类型
* args() 用于匹配当前执行的方法传入的参数为指定类型的执行方法
*/
@Pointcut("execution(表达式)")
public void LogAopAspect() {
}
@Before("LogAopAspect()")//前置处理
public void doBefore(JoinPoint joinPoint) {
System.out.println("doBefore");
}
@After("LogAopAspect()")//后置处理
public void doAfter(JoinPoint joinPoint) {
System.out.println("doAfter");
}
@AfterReturning("LogAopAspect()")//方法返回前
public void doAfterReturning(JoinPoint joinPoint) {
System.out.println("doAfterReturning");
}
@AfterThrowing("LogAopAspect()")//异常抛出前
public void doAfterThrowing(JoinPoint joinPoint) {
System.out.println("doAfterThrowing");
}
@Around("LogAopAspect()")//环绕通知
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("doAround");
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
String requestURI = request.getRequestURI();
System.out.println(requestURI);
String header = request.getHeader("Authorization");
Object[] args = joinPoint.getArgs();
String kind = joinPoint.getKind();
Signature signature = joinPoint.getSignature();
Class declaringType = signature.getDeclaringType();
SourceLocation sourceLocation = joinPoint.getSourceLocation();
Object target = joinPoint.getTarget();
Class<?> aClass = target.getClass();
Method[] declaredMethods = aClass.getDeclaredMethods();
Field[] declaredFields = aClass.getDeclaredFields();
JoinPoint.StaticPart staticPart = joinPoint.getStaticPart();
Object proceed = joinPoint.proceed();
return proceed;
}
}
- @Aspect:表明这个类是一个切面,Spring会将它作为切面处理。
- @Component:将该类标记为Spring的组件,这样它会被自动扫描并注册为Spring的Bean。
- @Pointcut:用于定义切点,这里需要替换
表达式
为实际的切点表达式,表示在哪些方法上应用切面。 - execution():是切点表达式,用于匹配方法执行的连接点。可以指定方法的修饰符、返回类型、类名、方法名、参数类型等。
- @Before:在目标方法执行之前执行的通知。
- JoinPoint:表示连接点,提供对当前执行的目标方法的信息,如方法参数、方法名等。
- @After:在目标方法执行之后执行的通知,无论方法是否抛出异常,都会被执行。
- @AfterReturning:在目标方法成功返回后执行的通知,可以获取返回值。
- @AfterThrowing:在目标方法抛出异常后执行的通知,可以处理异常。
- @Around:包裹了目标方法的执行,可以在调用目标方法前后添加自定义逻辑。
- ProceedingJoinPoint:扩展了
JoinPoint
,可以调用proceed()
方法来执行目标方法。 - HttpServletRequest:从
RequestContextHolder
中获取当前的请求信息,可以访问请求的URI和头信息(如Authorization)。 - 方法信息:
joinPoint.getArgs()
:获取目标方法的参数。joinPoint.getKind()
:获取连接点的类型(如方法调用)。signature.getDeclaringType()
:获取目标方法声明的类型。joinPoint.getTarget()
:获取当前连接点的目标对象。aClass.getDeclaredMethods()
和aClass.getDeclaredFields()
:获取目标对象的所有方法和字段。
调用proceed()
方法执行目标方法,并返回其结果。这一结果可以用于后续的处理或直接返回。
注意:启动类需要添加 @EnableAspectJAutoProxy 开启AOP自动注入
三、扩展知识(execution()表达式)
1.定义
execution() 是Spring AOP中用于定义切点的表达式。它可以根据方法的修饰符、返回类型、类名、方法名以及参数类型来匹配特定的方法。下面是execution()表达式的详细编写规则和示例。
2. 语法结构
execution(modifier-pattern return-type-pattern declaring-type-pattern name-pattern(param-pattern) throws-pattern)
- modifier-pattern:匹配方法的修饰符(可选)。可以是public、protected、private等,省略时匹配任意修饰符。
- return-type-pattern:匹配方法的返回类型。可以是具体类型,如void、String,或者使用*表示任意类型。
- declaring-type-pattern:匹配方法声明所在的类。可以是具体类名、包名或使用..表示该包及其子包。
- name-pattern:匹配方法名。可以使用*作为通配符,表示任意方法名。
- param-pattern:匹配方法的参数类型和数量。可以使用(..)表示任意参数,或者指定具体参数类型。
- throws-pattern:匹配抛出的异常类型(可选)。省略时匹配任何异常。
3.示例
3.1 匹配所有方法
@Pointcut("execution(* *(..))")
-解释:匹配任何类中的任何方法,不论返回类型、方法名和参数。
3.2 匹配某个包中的所有方法
@Pointcut("execution(* com.example.service..*(..))")
-解释:匹配com.example.service包及其子包中所有方法。
3.3 匹配特定类中的所有方法
@Pointcut("execution(* com.example.service.MyService.*(..))")
-解释:匹配MyService类中的所有方法。
3.4 匹配特定返回类型的方法
@Pointcut("execution(String com.example.service.MyService.get*(..))")
-解释:匹配返回类型为String且方法名以get开头的MyService类中的所有方法。
3.5 匹配特定参数的方法
@Pointcut("execution(* com.example.service.MyService.processOrder(..))")
-解释:匹配MyService类中名为processOrder的方法,可以有任意数量的参数。
3.6 匹配特定修饰符的方法
@Pointcut("execution(public * com.example.service.MyService.*(..))")
-解释:匹配MyService类中所有public修饰的方法。
3.7 匹配抛出特定异常的方法
@Pointcut("execution(* com.example.service.MyService.*(..) throws IOException)")
-解释:匹配MyService类中所有方法,但仅限于抛出IOException的情况。
4. 组合表达式
可以使用逻辑运算符&&(与)、||(或)和!(非)来组合多个切点表达式。例如:
@Pointcut("execution(* com.example.service.*.*(..)) && !execution(* com.example.service.MyService.*(..))")
-解释:匹配com.example.service包中所有方法,但不匹配MyService类中的方法。