You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何获取应用中所有Controller及RequestMapping方法并识别Controller注解

How to Identify Controllers and List Their Request Mapping Methods in Your Application

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 @Controller and @RestController since the latter is a combination of @Controller and @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 @RequestMapping itself) since GetMapping, 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:

  1. Add the Actuator dependency to your pom.xml (or build.gradle):
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
  1. Enable the mappings endpoint in application.properties:
management.endpoints.web.exposure.include=mappings
  1. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 07:56:51