AOP实现
切点(Pointcut)
- 切点的作用是定义在何处应用横切关注点。
- 通过切点表达式,我们可以精确地指定需要应用横切逻辑的方法、类或包等。
- 常见的切点表达式有:方法签名匹配、注解匹配、控制流匹配、静态分析等。
通知(Advice)
- 通知的作用是定义在连接点上执行的具体操作。
- 通知包括前置通知、后置通知、环绕通知等,用于在目标方法执行前、执行后或执行期间插入横切逻辑。
- 通知的实现依赖于动态代理、字节码增强或编译时织入等底层技术。
切面(Aspect)
- 切面的作用是将切点和通知组合在一起。
- 切面定义了在何处(切点)应用何种操作(通知)。
- 切面的实现也依赖于动态代理、字节码增强或编译时织入等底层技术。
// 定义切面 @Aspect public class LoggingAspect { // 使用 execution 表达式定义切点 @Pointcut("execution(* com.example.service.*.*(..))") public void serviceMethodPointcut() { } // 前置通知 @Before("serviceMethodPointcut()") public void logBefore(JoinPoint joinPoint) { System.out.println("Calling method: " + joinPoint.getSignature().getName()); } // 后置通知 @AfterReturning(pointcut = "serviceMethodPointcut()", returning = "result") public void logAfter(JoinPoint joinPoint, Object result) { System.out.println("Method returned: " + result); } // 环绕通知 @Around("serviceMethodPointcut()") public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("Around: before method execution"); Object result = joinPoint.proceed(); System.out.println("Around: after method execution"); return result; } } // 定义目标对象 @Service public class MyService { public String getMessage() { return "Hello, Spring AOP!"; } public int add(int a, int b) { return a + b; } } // 配置 AOP @Configuration @EnableAspectJAutoProxy public class AppConfig { @Bean public LoggingAspect loggingAspect() { return new LoggingAspect(); } } // 使用目标对象 public class Main { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); MyService service = context.getBean(MyService.class); System.out.println(service.getMessage()); System.out.println(service.add(2, 3)); context.close(); } }
底层的切点实现
在 Java 中,切点(Pointcut)是用于定义在何处应用横切关注点的表达式。底层的切点实现主要依赖于 Java 反射机制。通常有以下几种常见的切点实现方式:
- 方法签名匹配:使用正则表达式或通配符匹配方法签名。
import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; @Aspect public class MethodSignatureMatchingAspect { @Pointcut("execution(* com.example.myapp.service..*(*)) && args(..)") public void allServiceMethods() {} @Before("allServiceMethods()") public void logMethodCall(JoinPoint joinPoint) { System.out.println("Calling method: " + joinPoint.getSignature()); } }
- 注解匹配:扫描类或方法上的自定义注解。
import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; @Aspect public class AnnotationMatchingAspect { @Pointcut("@annotation(com.example.myapp.annotations.LogMethodCall)") public void loggedMethods() {} @Before("loggedMethods()") public void logMethodCall(JoinPoint joinPoint) { System.out.println("Calling method: " + joinPoint.getSignature()); } }
- 静态分析:通过静态字节码分析确定切点。
import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; @Aspect public class StaticAnalysisMatchingAspect { @Pointcut("execution(public * com.example.myapp.service.*.*(int, String))") public void publicServiceMethodsWithIntAndStringArgs() {} @Before("publicServiceMethodsWithIntAndStringArgs()") public void logMethodCall(JoinPoint joinPoint) { System.out.println("Calling method: " + joinPoint.getSignature()); } }
底层的通知实现
通知(Advice)是切面在特定连接点上的动作,主要包括前置通知、后置通知、环绕通知等。底层的通知实现通常依赖于以下技术:
- 动态代理(JDK 动态代理或 CGLIB 动态代理)
- 字节码增强(如 ASM、Javassist 等字节码操作库)
- 编译时织入(如 AspectJ 的编译期织入)
通知的实现通常需要在目标方法执行前后或周围进行介入,以实现特定的横切逻辑。
底层的切面实现
切面(Aspect)是通知和切点的结合。底层的切面实现主要包括以下几种方式:
- 基于代理的切面实现
- 使用 JDK 动态代理或 CGLIB 动态代理创建代理对象
- 在代理对象中织入通知逻辑
- 基于字节码增强的切面实现
- 使用字节码操作库(如 ASM、Javassist)直接修改目标类的字节码
- 在目标类的字节码中织入通知逻辑
- 基于编译时织入的切面实现
- 使用 AspectJ 等 AOP 框架在编译期对目标类进行织入
- 在编译期将通知逻辑织入到目标类中
Spring中ProxyFactory 用来创建代理
如果指定了接口,且 proxyTargetClass = false,使用 JdkDynamicAopProxy
如果没有指定接口,或者 proxyTargetClass = true,使用 ObjenesisCglibAopProxy
例外:如果目标是接口类型或已经是 Jdk 代理,使用 JdkDynamicAopProxy