Files
springboot-demo/src/main/java/com/example/demo/controller/AdvancedLabController.java
2026-03-23 13:05:44 +08:00

181 lines
7.5 KiB
Java

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<Map<String, Object>> reflectionRoutes() {
List<Map<String, Object>> routes = handlerMapping.getHandlerMethods().entrySet().stream()
.map(this::toRouteView)
.sorted((left, right) -> left.get("path").toString().compareTo(right.get("path").toString()))
.toList();
List<Map<String, Object>> 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<Map<String, Object>> reflectUserModel() {
List<Map<String, Object>> 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<Map<String, Object>> 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<CompletableFuture<Map<String, Object>>> jobs = createJobs(safeTasks, counter, executor);
CompletableFuture.allOf(jobs.toArray(CompletableFuture[]::new)).join();
List<Map<String, Object>> 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<Map<String, Object>> 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<CompletableFuture<Map<String, Object>>> 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.<String, Object>of(
"task", index,
"thread", Thread.currentThread().getName(),
"counterAfterIncrement", current
);
}, executor))
.toList();
}
private Map<String, Object> toRouteView(Map.Entry<RequestMappingInfo, HandlerMethod> 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<String, Object> summarizeClass(Class<?> type) {
List<Map<String, Object>> 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<String, Object> 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<String, Object> 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()
);
}
}