如何获取应用中所有Controller及RequestMapping方法并识别Controller注解
Great question! Assuming you're working with Spring/Spring Boot (since @Controller is a core Spring annotation), here are several practical, actionable ways to achieve this—from building a custom solution to using built-in tools:
Method 1: Leverage Spring's ApplicationContext
Spring's container manages all beans annotated with @Controller or @RestController, so you can directly fetch these beans and parse their annotations via reflection:
import org.springframework.context.ApplicationContext; import org.springframework.stereotype.Component; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*; import java.lang.reflect.Method; import java.util.Map; @Component public class ControllerInspector { private final ApplicationContext appContext; // Inject ApplicationContext via constructor public ControllerInspector(ApplicationContext appContext) { this.appContext = appContext; } public void printAllControllersAndEndpoints() { // Fetch all beans annotated with @Controller or @RestController Map<String, Object> controllerBeans = appContext.getBeansWithAnnotation(Controller.class); controllerBeans.putAll(appContext.getBeansWithAnnotation(RestController.class)); for (Map.Entry<String, Object> entry : controllerBeans.entrySet()) { Class<?> controllerClass = entry.getValue().getClass(); // Handle CGLIB proxies (Spring often wraps beans in proxies for AOP) if (controllerClass.getName().contains("$$EnhancerBySpringCGLIB$$")) { controllerClass = controllerClass.getSuperclass(); } System.out.println("=== Controller: " + controllerClass.getSimpleName() + " ==="); String classBasePath = getClassLevelRequestMapping(controllerClass); if (!classBasePath.isEmpty()) { System.out.println("Base Path: " + classBasePath); } // Scan all methods for request mapping annotations for (Method method : controllerClass.getDeclaredMethods()) { String endpointPath = ""; String requestMethod = ""; // Check for all request mapping variants if (method.isAnnotationPresent(GetMapping.class)) { GetMapping anno = method.getAnnotation(GetMapping.class); endpointPath = String.join(", ", anno.value()); requestMethod = "GET"; } else if (method.isAnnotationPresent(PostMapping.class)) { PostMapping anno = method.getAnnotation(PostMapping.class); endpointPath = String.join(", ", anno.value()); requestMethod = "POST"; } else if (method.isAnnotationPresent(RequestMapping.class)) { RequestMapping anno = method.getAnnotation(RequestMapping.class); endpointPath = String.join(", ", anno.value()); requestMethod = anno.method().length > 0 ? anno.method()[0].name() : "ANY"; } // Add checks for PutMapping, DeleteMapping, etc., as needed if (!endpointPath.isEmpty()) { // Combine class-level and method-level paths String fullPath = classBasePath.isEmpty() ? endpointPath : classBasePath + endpointPath; System.out.printf(" - Method: %s | Full Path: %s | Request Type: %s%n", method.getName(), fullPath, requestMethod); } } System.out.println(); } } // Extract class-level @RequestMapping path private String getClassLevelRequestMapping(Class<?> controllerClass) { RequestMapping classAnno = controllerClass.getAnnotation(RequestMapping.class); return classAnno != null ? String.join(", ", classAnno.value()) : ""; } }
Key Notes:
- We handle both
@Controllerand@RestControllersince the latter is a combination of@Controllerand@ResponseBody. - CGLIB proxy detection ensures we get the actual controller class, not Spring's proxy wrapper.
- We parse all common request mapping annotations (not just
@RequestMappingitself) sinceGetMapping,PostMapping, etc., are widely used.
Method 2: Manual Classpath Scanning
If you need more control (or aren't relying on a fully initialized Spring context), use Spring's ClassPathScanningCandidateComponentProvider to scan specific packages:
import org.springframework.core.type.filter.AnnotationTypeFilter; import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider; import org.springframework.web.bind.annotation.Controller; import org.springframework.web.bind.annotation.RestController; import java.lang.reflect.Method; import java.util.Set; public class ManualControllerScanner { public void scanControllers(String basePackage) { ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false); // Add filters to pick up controller annotations scanner.addIncludeFilter(new AnnotationTypeFilter(Controller.class)); scanner.addIncludeFilter(new AnnotationTypeFilter(RestController.class)); // Scan the target package Set<org.springframework.core.type.classreading.MetadataReader> candidates = scanner.findCandidateComponents(basePackage); for (org.springframework.core.type.classreading.MetadataReader reader : candidates) { try { Class<?> controllerClass = Class.forName(reader.getClassMetadata().getClassName()); System.out.println("Found Controller: " + controllerClass.getSimpleName()); // Reuse the method parsing logic from Method 1 here // ... } catch (ClassNotFoundException e) { e.printStackTrace(); } } } }
This is ideal if you want to restrict scanning to specific packages or add custom filtering rules.
Method 3: Use Spring Boot Actuator (No Custom Code Needed)
If you're using Spring Boot, the easiest way is to enable the Actuator mappings endpoint—it gives you a ready-made JSON view of all request mappings:
- Add the Actuator dependency to your
pom.xml(orbuild.gradle):
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
- Enable the
mappingsendpoint inapplication.properties:
management.endpoints.web.exposure.include=mappings
- Start your app and visit
http://localhost:8080/actuator/mappings—you'll get a detailed JSON output with all controllers, methods, paths, and request types.
Important Considerations
- Derived Annotations: Don't forget to handle
GetMapping,PostMapping, etc.—they're all specialized versions of@RequestMapping. - Path Merging: Always combine class-level and method-level paths to get the full endpoint URL.
- Security: If using Actuator, make sure to secure the endpoint in production (e.g., add authentication).
内容的提问来源于stack exchange,提问作者Abhinav jha

