如何在Spring AOP中获取控制器方法的token参数以简化重复代码
Great call extracting that repetitive session validation logic into an AOP aspect—it’s exactly the kind of problem AOP was made to solve! Let’s walk through how to reliably grab the token parameter from your controller methods and execute your validation logic.
Step 1: Create Your AOP Aspect Class
We’ll build an aspect that targets all methods in your AdminController, extracts the token, and runs your validation. The key here is to identify the token either by its @PathVariable annotation or parameter name (with a small build config tweak).
Here’s the complete aspect:
@Component @Aspect public class AdminSessionValidationAspect { private final Map<String, OurSession> clientSessions; // Inject your clientSessions map via constructor public AdminSessionValidationAspect(Map<String, OurSession> clientSessions) { this.clientSessions = clientSessions; } @Before("execution(* com.example.couponproject.controller.AdminController.*(..))") public void validateAdminSession(JoinPoint joinPoint) { MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); Method targetMethod = methodSignature.getMethod(); Object[] methodArgs = joinPoint.getArgs(); Annotation[][] paramAnnotations = targetMethod.getParameterAnnotations(); String token = null; // 1. Find the token parameter using its @PathVariable annotation for (int i = 0; i < paramAnnotations.length; i++) { for (Annotation annotation : paramAnnotations[i]) { if (annotation instanceof PathVariable) { PathVariable pathVar = (PathVariable) annotation; if ("token".equals(pathVar.value()) || "token".equals(pathVar.name())) { token = (String) methodArgs[i]; break; } } } if (token != null) break; } // 2. Handle missing token (fail fast) if (token == null) { throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "UNAUTHORIZED login"); } // 3. Run your original validation logic OurSession session = clientSessions.get(token); if (session == null) { throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "UNAUTHORIZED login"); } AdminFacade admin = (AdminFacade) session.getFacade(); long currentTime = System.currentTimeMillis(); if (currentTime - session.getLastAccessed() > 1000 * 60 * 30) { throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "UNAUTHORIZED login"); } session.setLastAccessed(currentTime); // 4. Pass the AdminFacade to the controller method (optional, using ThreadLocal) AdminContextHolder.setAdmin(admin); } // Clean up ThreadLocal to avoid memory leaks @After("execution(* com.example.couponproject.controller.AdminController.*(..))") public void clearAdminContext() { AdminContextHolder.clear(); } }
Step 2: Simplify Your Controller Methods
Now you can strip out all the repetitive validation code from your controller methods. Use a ThreadLocal helper to access the pre-validated AdminFacade:
First, create the helper class:
public class AdminContextHolder { private static final ThreadLocal<AdminFacade> ADMIN_HOLDER = new ThreadLocal<>(); public static void setAdmin(AdminFacade admin) { ADMIN_HOLDER.set(admin); } public static AdminFacade getAdmin() { return ADMIN_HOLDER.get(); } public static void clear() { ADMIN_HOLDER.remove(); } }
Then update your addCompany method:
@PostMapping("addCompany/{token}") public ResponseEntity<?> addCompany(@PathVariable String token, @RequestBody Company company){ try { AdminFacade admin = AdminContextHolder.getAdmin(); admin.addCompany(company); return ResponseEntity.ok(company); } catch (CompanyAlreadyExistsException e) { return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage()); } }
Alternative: Use Parameter Names (Requires Build Config)
If you prefer to match the parameter by its name (token) instead of the annotation, you need to enable parameter name retention in your build tool. For Maven, add this to your pom.xml:
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.1</version> <configuration> <parameters>true</parameters> <source>11</source> <!-- Match your JDK version --> <target>11</target> </configuration> </plugin> </plugins> </build>
Then replace the annotation-based token lookup with this simpler code in the aspect:
String[] paramNames = methodSignature.getParameterNames(); for (int i = 0; i < paramNames.length; i++) { if ("token".equals(paramNames[i])) { token = (String) methodArgs[i]; break; } }
Key Notes
- Using
ResponseStatusExceptionlets Spring automatically convert the exception to the correctResponseEntityresponse, so you don’t have to handle that in your controller. - The
@Afteradvice cleans up theThreadLocalto prevent memory leaks, which is critical for long-running applications. - This approach works regardless of where the
tokenparameter appears in your controller method’s parameter list.
内容的提问来源于stack exchange,提问作者wedew2

