package com.example.demo.controller; import com.example.demo.common.ApiResponse; import com.example.demo.security.LearningJwtUtil; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.mvc.method.RequestMappingInfo; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.time.Duration; import java.time.Instant; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicInteger; @RestController @RequestMapping("/api/lab") public class AdvancedLabController { private final RequestMappingHandlerMapping handlerMapping; private final LearningJwtUtil jwtUtil; public AdvancedLabController( @Qualifier("requestMappingHandlerMapping") RequestMappingHandlerMapping handlerMapping, LearningJwtUtil jwtUtil ) { this.handlerMapping = handlerMapping; this.jwtUtil = jwtUtil; } @GetMapping("/reflection/routes") public ApiResponse> reflectionRoutes() { List> routes = handlerMapping.getHandlerMethods().entrySet().stream() .map(this::toRouteView) .sorted((left, right) -> left.get("path").toString().compareTo(right.get("path").toString())) .toList(); List> classSummaries = List.of( summarizeClass(UserController.class), summarizeClass(LearnController.class), summarizeClass(AopEventController.class), summarizeClass(com.example.demo.controller.auth.LearningAuthController.class) ); return ApiResponse.ok(Map.of( "routeCount", routes.size(), "routes", routes, "classSummaries", classSummaries, "tip", "Use reflection to connect annotations, method signatures, and runtime route registration." )); } @GetMapping("/reflection/user-model") public ApiResponse> reflectUserModel() { List> fields = Arrays.stream(com.example.demo.model.User.class.getDeclaredFields()) .map(this::toFieldView) .toList(); return ApiResponse.ok(Map.of( "className", com.example.demo.model.User.class.getName(), "fieldCount", fields.size(), "fields", fields, "tip", "Reflection makes frameworks possible because metadata can be discovered at runtime." )); } @GetMapping("/concurrency/simulate") public ApiResponse> simulateConcurrency( @RequestParam(defaultValue = "12") int tasks, @RequestParam(defaultValue = "4") int poolSize ) { int safeTasks = Math.max(1, Math.min(tasks, 32)); int safePoolSize = Math.max(1, Math.min(poolSize, 8)); ExecutorService executor = Executors.newFixedThreadPool(safePoolSize); AtomicInteger counter = new AtomicInteger(); Instant start = Instant.now(); try { List>> jobs = createJobs(safeTasks, counter, executor); CompletableFuture.allOf(jobs.toArray(CompletableFuture[]::new)).join(); List> results = jobs.stream() .map(CompletableFuture::join) .toList(); return ApiResponse.ok(Map.of( "tasks", safeTasks, "poolSize", safePoolSize, "finalCounter", counter.get(), "durationMs", Duration.between(start, Instant.now()).toMillis(), "sample", results.stream().limit(5).toList(), "tip", "AtomicInteger keeps the shared counter safe when many tasks run in parallel." )); } finally { executor.shutdownNow(); } } @GetMapping("/jwt/claims") public ApiResponse> jwtClaims(@RequestHeader("Authorization") String authorization) { String token = authorization.substring(7); return ApiResponse.ok(Map.of( "subject", jwtUtil.username(token), "claims", jwtUtil.claims(token), "tip", "JWT claims can be parsed without a database lookup, which is one reason token auth scales well." )); } private List>> createJobs(int tasks, AtomicInteger counter, ExecutorService executor) { return java.util.stream.IntStream.range(0, tasks) .mapToObj(index -> CompletableFuture.supplyAsync(() -> { int current = counter.incrementAndGet(); try { Thread.sleep(20L + (index % 3) * 10L); } catch (InterruptedException exception) { Thread.currentThread().interrupt(); } return Map.of( "task", index, "thread", Thread.currentThread().getName(), "counterAfterIncrement", current ); }, executor)) .toList(); } private Map toRouteView(Map.Entry entry) { HandlerMethod handlerMethod = entry.getValue(); return Map.of( "path", entry.getKey().getPatternValues().toString(), "methods", entry.getKey().getMethodsCondition().getMethods().toString(), "handler", handlerMethod.getBeanType().getSimpleName() + "#" + handlerMethod.getMethod().getName(), "parameters", Arrays.stream(handlerMethod.getMethod().getParameters()) .map(parameter -> parameter.getType().getSimpleName()) .toList() ); } private Map summarizeClass(Class type) { List> methods = Arrays.stream(type.getDeclaredMethods()) .filter(method -> !method.isSynthetic()) .map(this::toMethodView) .toList(); return Map.of( "className", type.getSimpleName(), "annotations", Arrays.stream(type.getAnnotations()).map(annotation -> annotation.annotationType().getSimpleName()).toList(), "methodCount", methods.size(), "methods", methods ); } private Map toMethodView(Method method) { return Map.of( "name", method.getName(), "returnType", method.getReturnType().getSimpleName(), "parameterCount", method.getParameterCount(), "annotations", Arrays.stream(method.getAnnotations()).map(Annotation::annotationType).map(Class::getSimpleName).toList() ); } private Map toFieldView(Field field) { return Map.of( "name", field.getName(), "type", field.getType().getSimpleName(), "annotations", Arrays.stream(field.getAnnotations()).map(Annotation::annotationType).map(Class::getSimpleName).toList() ); } }