SpringBoot中AOP的使用
SpringBoot中AOP的使用引言基本使用依赖引入启用AspectJ定义切面类切点执行方法 execution()在特定类内 within()引用注解 annotation()组合切点 , ||, !使用bean名称 bean()通知前置通知Before Advice后置通知After Advice返回后通知After Returning Advice异常抛出通知After Throwing Advice环绕通知Around Advice引言AOP(面向切面编程)作为一种编程范式使得开发者可以将横切关注点如日志记录事务管理权限控制等从业务逻辑中分离出来从而实现代码的高度模块化增强代码的可维护性和重用性。随着项目规模的不断扩大和业务逻辑的日益复杂AOP成为高效组织和管理代码的方式之一。基本使用依赖引入确保Spring Boot项目中包含了Spring AOP的依赖。如果为Maven项目可以这样引入!-- Spring Boot Starter AOP --dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-aop/artifactId/dependency!-- AspectJ --dependencygroupIdorg.aspectj/groupIdartifactIdaspectjweaver/artifactIdversion1.9.7/version/dependencySpring Boot 1.x 版本不支持 AOP但从 Spring Boot 2.x 开始Spring Boot 支持 AOP。另外Spring Boot 的 spring-boot-starter-aop 依赖会自动引入 AspectJ 的相关依赖所以不需要手动添加 aspectjweaver。启用AspectJ在Spring Boot 主类或者配置类上使用 EnableAspectJAutoProxy 注解来启用对 AspectJ 的支持。如果没有添加这个注解Spring 容器将不会创建代理对象因此 AspectJ 的切面aspects将不会生效。AspectJ 是 Spring AOP面向切面编程的一种实现方式。Spring AOP 是 Spring 框架的一个模块有两种主要的实现方式基于代理的 AOPProxy-based AOP这是 Spring AOP 的默认实现方式。它使用 JDK 动态代理来创建代理对象拦截方法调用然后应用切面逻辑。这种方式适用于那些接口的实现因为 JDK 动态代理只能代理实现了接口的类。基于 AspectJ 的 AOPAspectJ 是一个强大的面向切面编程框架它提供了比 Spring AOP 更丰富的功能和更细粒度的控制。它使用特殊的编译时织入编译时和加载时织入或运行时代理来实现 AOP。AspectJ 允许使用 Aspect 注解来定义切面并使用 Before、After、AfterReturning、AfterThrowing 和 Around 等注解来定义切点。这是开发中常用的方式也是本文所讲述的方式。定义切面类创建一个切面类使用Aspect注解标记并在其中定义切点Pointcut和通知Advice。Javaimportorg.aspectj.lang.annotation.*;importorg.springframework.stereotype.Component;AspectComponentpublicclassLoggingAspect{Pointcut(execution(* com.example.yourpackage.service.*.*(..)))publicvoidallServiceMethods(){// 这是一个切点签名所有在service包下的方法都将被匹配}Before(allServiceMethods())publicvoidlogBefore(JoinPointjoinPoint){// 前置通知在方法执行前打印日志StringmethodNamejoinPoint.getSignature().getName();System.out.println(Executing: methodName);}AfterReturning(pointcutallServiceMethods(),returningresult)publicvoidlogAfterReturning(JoinPointjoinPoint,Objectresult){// 后置通知在方法成功执行后打印返回值StringmethodNamejoinPoint.getSignature().getName();System.out.println(methodName returned: result);}// 也可以直接将切点定义在通知中Before(execution(* com.example.yourpackage.service.*.*(..)))publicvoidlogBefore(JoinPointjoinPoint){// 前置通知在方法执行前打印日志StringmethodNamejoinPoint.getSignature().getName();System.out.println(Executing: methodName);}}注意事项通过添加Component 注解并确保所在包路径被 Spring Boot 的自动配置扫描覆盖。使用 AspectJ 的切点表达式时可以根据需要调整匹配规则以精确控制哪些方法或类受到切面的影响。切点在Spring Boot中使用AOP面向切面编程时切点Pointcut定义规则是用来确定何处执行通知Advice的表达式。切点定义了横切关注点即要拦截或增强的方法执行与应用程序代码之间的连接点。Spring AOP支持多种切点表达式语言最常用的是AspectJ表达式。以下是一些基本的AspectJ切点定义规则和示例执行方法 execution()语法: execution([修饰符] 返回类型 包名.类名.方法名(参数列表))切入所有公共方法execution(public * * (…))切入指定包下所有类的任意方法execution(* com.example.project.*.*(…))切入特定类的所有方法execution(* com.example.project.MyClass.*(…))切入特定类的以aa开头的方法execution(* com.example.project.MyClass.aa*(…))切入特定方法execution(void com.example.project.MyClass.myMethod(String, int))在特定类内 within()语法: within(包名.类名)仅拦截com.example.project包下的类within(com.example.project.*)引用注解 annotation()语法: annotation(注解全限定名)拦截带有特定注解的方法annotation(com.example.project.MyAnnotation)组合切点 , ||, ! 同时满足两个条件execution(* com.example.project.MyClass.myMethod(…)) annotation(com.example.project.MyAnnotation)拦截除特定方法外的所有方法execution(* com.example.project.MyClass.*(…)) !execution(void com.example.project.MyClass.excludeMe(…))使用bean名称 bean()语法: bean(BeanName)根据Bean名称匹配bean(myBeanName)配置示例在Spring Boot项目中你通常会在一个配置类中定义切面如下所示import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;import org.springframework.stereotype.Component;注意. .双点号双点号…是一个真正的通配符具有以下两种主要用途匹配任意数量和类型的参数:在方法签名中紧跟在方法名后的…表示可以匹配任意数量和任意类型的参数。例如execution(* com.example.MyClass.myMethod(…))会匹配myMethod方法的所有重载版本不管它们接受什么参数或参数的数量。匹配任意子包:在包名后面. .表示匹配指定包及其所有子包。例如execution(* com.example. .*.*(…))会匹配com.example包及其所有子包下的所有类的任何方法。通知在Spring AOP中通知Advice是指在切点Pointcut匹配的方法执行前后或异常抛出时执行的代码片段。Spring AOP支持五种类型的通知它们分别是前置通知Before Advice用途: 在目标方法执行之前执行。应用场景: 日志记录、权限检查、性能监控开始计时等。入参: 通常只有一个参数JoinPoint可以用来获取被调用方法的信息如方法名、参数等。Before(execution(* com.example.service.MyService.*(..)))publicvoidbeforeAdvice(JoinPointjoinPoint){System.out.println(Executing: joinPoint.getSignature().getName());}后置通知After Advice用途: 在目标方法执行之后执行无论方法是否正常结束。应用场景: 清理操作、资源释放、性能监控结束计时等。入参: 可选地接收一个JoinPoint参数。After(execution(* com.example.service.MyService.*(..)))publicvoidafterAdvice(){System.out.println(After method execution);}返回后通知After Returning Advice用途: 在目标方法正常执行完毕后执行。应用场景: 处理返回结果如日志记录、数据转换等。入参: 可以接收方法的返回值通过returning属性指定参数名以及可选的JoinPoint。AfterReturning(pointcutexecution(* com.example.service.MyService.someMethod(..)),returningresult)publicvoidafterReturningAdvice(Objectresult){System.out.println(Returned value: result);}异常抛出通知After Throwing Advice用途: 在方法抛出异常后执行。应用场景: 异常处理、记录错误日志、发送报警等。入参: 可以接收抛出的异常通过throwing属性指定参数名以及可选的JoinPoint。AfterThrowing(pointcutexecution(* com.example.service.MyService.*(..)),throwingex)publicvoidafterThrowingAdvice(Throwableex){System.out.println(Exception caught: ex.getMessage());}环绕通知Around Advice用途: 包围目标方法的执行允许在方法调用前后自定义行为甚至决定是否执行目标方法。应用场景: 统一处理事务、时间监控、日志记录等灵活性最高。入参: 接收一个ProceedingJoinPoint参数用来决定是否继续执行目标方法及获取其结果。Around(execution(* com.example.service.MyService.*(..)))publicObjectaroundAdvice(ProceedingJoinPointjoinPoint)throwsThrowable{Object[]argsjoinPoint.getArgs();// 获取方法执行参数System.out.println(Before method execution);try{ObjectresultjoinPoint.proceed(args);// 继续执行目标方法System.out.println(After method execution);returnresult;}catch(Exceptionex){System.out.println(Exception caught in around advice: ex.getMessage());throwex;// 重新抛出异常}}总结前置、后置、异常抛出通知主要用于简单场景如记录日志、异常处理等不直接干预方法执行。返回后通知适用于对方法返回结果进行进一步处理。环绕通知是最灵活的它能控制方法的执行并在前后执行自定义操作适合需要更细粒度控制的场景如事务管理。愿你我都能在各自的领域里不断成长勇敢追求梦想同时也保持对世界的好奇与善意。

相关新闻