181 lines
7.5 KiB
Java
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()
|
|
);
|
|
}
|
|
}
|