controller接口日志输出

在使用AOP进行日志记录时,由于在尝试获取切点数据时抛出的Throwable异常先于ControllerAdvice的异常处理,导致ControllerAdvice只能捕获到Exception。文章探讨了处理流程的顺序,即Filter>Interceptor>ControllerAdvice>AOP(接收时),以及AOP>ControllerAdvice>Interceptor>Filter(返回时),并指出这一顺序是问题的根本原因。

问题出现:使用aop中around进行日志输出时,ControllerAdvice进行异常捕获时只能捕获exception异常

原因:controller执行完成之后,使用aop获取切点数据时try catch抛出了Throwable异常导致controller进行捕获时只能捕获到exception异常

根本原因为执行顺序

当接收数据时为:Filter过滤器 > Interceptor拦截器 > ControllerAdvice > AOP。

当返回数据时为:AOP > ControllerAdvice > Interceptor拦截器 > Filter过滤器

<think>我们已经讨论过如何使用JoinPoint获取方法的完整名称(包括包名、类名和方法名)。现在用户的需求更具体:获取完整的Controller接口名称。这里的“接口名称”可能指Controller方法的完整路径(如URL路径),也可能指Java方法的完整签名(包名+类名+方法名)。结合引用[1]和[3],用户可能是在记录操作日志时需要展示Controller方法的标识信息,并且希望避免手动维护枚举。 根据引用[3]:“通过自定义注解和AOP实现打印所有controller接口的出入参数”,我们可以推断用户主要关注的是Controller层的方法。因此,获取Controller方法的完整信息(如类名、方法名、URL路径等)是关键。 ### 解决方案 在Spring中,Controller接口名称通常有两种含义: 1. **Java方法的完整签名**:包名+类名+方法名(如`com.example.controller.UserController.createUser`)。 2. **HTTP请求的路径**:如`/api/users`(通过`@RequestMapping`等注解定义)。 用户问题中提到的“完整controller接口名称”可能同时包含这两者,也可能仅指其中之一。但根据引用[1],用户需要记录方法的中文名称并展示到页面,因此更可能关注的是Java方法的完整签名(因为URL路径通常不包含中文描述)。此外,如果用户想获取的是URL路径,则需要通过Spring的`RequestMappingHandlerMapping`等机制,而非直接通过`JoinPoint`。 ### 方法一:获取Controller方法的完整Java签名(推荐) 使用`JoinPoint`获取Controller方法的完整Java签名(包名+类名+方法名)是最直接的方式: ```java import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; @Aspect @Component public class ControllerAspect { @Before("execution(* com.example.controller..*.*(..))") // 针对controller包下的所有方法 public void logControllerMethod(JoinPoint joinPoint) { // 获取声明类型名(即Controller类的全限定名) String className = joinPoint.getSignature().getDeclaringTypeName(); // 获取方法名 String methodName = joinPoint.getSignature().getName(); // 拼接完整方法签名 String fullMethodSignature = className + "." + methodName; System.out.println("Controller方法完整签名: " + fullMethodSignature); } } ``` ### 方法二:获取Controller方法的URL路径(需结合Spring Web) 如果需要获取HTTP路径(如`/api/users`),则必须从Spring的`RequestMappingInfo`中提取。这种方式更复杂,但能获取实际接口路径: ```java import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; import javax.servlet.http.HttpServletRequest; import java.util.Optional; @Aspect @Component public class ControllerAspect { @Autowired private RequestMappingHandlerMapping handlerMapping; @Before("execution(* com.example.controller..*.*(..))") public void logControllerUrl(JoinPoint joinPoint) throws Exception { HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest(); String requestUri = request.getRequestURI(); // 获取请求URI(如/api/users) System.out.println("接口请求路径: " + requestUri); // 如果需要更详细的映射信息(如匹配的Controller方法),可以使用handlerMapping // 但注意:在AOP中直接使用handlerMapping可能因请求未完全处理而受限,需谨慎 } } ``` **注意事项**: - 方法二依赖HTTP请求上下文(`RequestContextHolder`),因此仅适用于Web请求场景,且需确保在请求线程中执行(如`@Before`通知)。 - 获取的URI可能包含上下文路径(如`/app/api/users`),需根据实际情况处理。 ### 结合自定义注解获取中文名称(参考引用[1]) 引用[1]提到需要“方法的中文名称”,这通常需要自定义注解实现。例如定义`@OperationLog`注解: ```java @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface OperationLog { String value(); // 中文名称 } ``` 在Controller方法上使用: ```java @RestController public class UserController { @OperationLog("创建用户") @PostMapping("/users") public void createUser() { /* ... */ } } ``` 在切面中获取注解值: ```java @Before("execution(* com.example.controller..*.*(..)) && @annotation(operationLog)") public void logOperation(JoinPoint joinPoint, OperationLog operationLog) { String chineseName = operationLog.value(); String fullMethodName = joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName(); System.out.println("操作名称: " + chineseName + ", 方法: " + fullMethodName); } ``` ### 总结 - **获取Java签名**:直接通过`JoinPoint.getSignature()`组合`getDeclaringTypeName()`和`getName()`。 - **获取URL路径**:通过`RequestContextHolder`获取`HttpServletRequest`提取URI。 - **获取中文名称**:结合自定义注解实现,避免手动维护枚举[^1]。 §§相关问题§§ 1. 如何在AOP中同时获取Controller方法的URL路径和Java方法名? 2. 使用JoinPoint获取Controller方法的参数有哪些最佳实践? 3. 如何避免在AOP中频繁使用反射获取注解信息带来的性能问题? [^1]: 在日常的开发中,我们有时需要记录用户的操作日志并展示到页面上,因此我们需要知道用户都调用了哪些方法,要展示到页面上,肯定是需要方法名称(中文名称),甚至包括其注释,参数等。 [^2]: 切面aspect:指定就是切面类,切面类会管理着切点、通知。通知advice:需要增强到业务方法中的公共代码。 [^3]: 通过自定义注解和AOP实现打印所有controller接口的出入参数。 </think>### 使用JoinPoint获取完整Controller接口名称的方法 在Spring AOP中,通过JoinPoint可以获取Controller方法的完整接口名称(包括包名、类名和方法名)。以下是具体实现方法,结合引用[1]和[3]中关于记录操作日志和打印接口参数的需求: #### 核心步骤 1. **声明切面并定义切入点** 使用`@Pointcut`定位所有Controller方法: ```java @Pointcut("within(@org.springframework.stereotype.Controller *) || " + "within(@org.springframework.web.bind.annotation.RestController *)") public void controllerMethods() {} ``` 2. **使用JoinPoint获取完整名称** 在通知方法中获取方法签名并组合完整名称: ```java @Before("controllerMethods()") public void logControllerMethod(JoinPoint joinPoint) { // 获取方法签名 MethodSignature signature = (MethodSignature) joinPoint.getSignature(); // 组合完整接口名称:包名+类名+方法名 String fullName = joinPoint.getTarget().getClass().getName() // 完整类名 + "." + signature.getName(); // 方法名 System.out.println("Controller接口完整名称: " + fullName); } ``` #### 完整切面实现(结合引用[3]的自定义注解) ```java import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.stereotype.Component; @Aspect @Component public class ControllerLogAspect { // 定义切入点:所有Controller类的方法 @Pointcut("within(@org.springframework.stereotype.Controller *) || " + "within(@org.springframework.web.bind.annotation.RestController *)") public void controllerPointcut() {} // 前置通知记录接口名称 @Before("controllerPointcut()") public void logControllerName(JoinPoint joinPoint) { String className = joinPoint.getTarget().getClass().getName(); String methodName = ((MethodSignature) joinPoint.getSignature()).getName(); String fullInterfaceName = className + "." + methodName; // 实际应用中可替换为日志记录 System.out.println("[控制器接口] " + fullInterfaceName); } } ``` #### 关键说明 1. **`joinPoint.getTarget()`** 获取目标Controller实例(如`com.example.UserController`),比`getDeclaringTypeName()`更准确,能避免代理类问题[^3] 2. **类型转换** 使用`(MethodSignature) joinPoint.getSignature()`获取详细方法信息 3. **结合自定义注解(引用[1][3])** 若需记录中文接口名,可添加自定义注解: ```java // 自定义注解 @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface OperationLog { String value(); // 中文接口名称 } // 切面中获取注解 Method method = ((MethodSignature) joinPoint.getSignature()).getMethod(); OperationLog anno = method.getAnnotation(OperationLog.class); if(anno != null) { System.out.println("接口中文名称: " + anno.value()); } ``` #### 输出示例 当访问`UserController.getUser()`时: ``` [控制器接口] com.example.controller.UserController.getUser ``` ### 注意事项 1. **避免代理类问题** 使用`joinPoint.getTarget()`代替`joinPoint.getTarget().getClass()`可避免Spring AOP代理类干扰 2. **性能优化** 在频繁调用的接口中,建议缓存`MethodSignature`对象避免重复反射操作[^2] 3. **接口路径获取** 若需要URL路径而非Java方法名,可通过注入`HttpServletRequest`获取: ```java ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()) .getRequest().getRequestURI(); ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值