使用 Spring Boot 3.0.2,将问题详细信息(例如,spring.mvc.problemdetails.enabled=true
)与代理(例如,由于 AOP)结合起来不起作用,因为类(在和ProblemDetailsExceptionHandler
中重复)是。servlet
reactive
final
我想对返回的问题详细信息执行某些操作(例如日志记录)。为此,我ExceptionHandler
使用 AspectJ 拦截返回值。
考虑以下尝试拦截ExceptionHandler
s 以记录ErrorResponse
详细信息的示例:
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.http.ResponseEntity;
import org.springframework.lang.Nullable;
import org.springframework.web.ErrorResponse;
@EnableAspectJAutoProxy
@WebMvcTest(value = ProblemTest.TestConfig.class, properties = "spring.mvc.problemdetails.enabled=true")
class ProblemTest {
@Test
void contextLoads() {}
@Configuration
static class TestConfig {
@Bean
ProblemLoggingAspect problemLoggingAspect() {
return new ProblemLoggingAspect();
}
}
@Aspect
static class ProblemLoggingAspect {
@AfterReturning(
pointcut = "@annotation(org.springframework.web.bind.annotation.ExceptionHandler)",
returning = "returnValue")
public void exceptionHandlerIntercept(JoinPoint joinPoint, Object returnValue) {
ErrorResponse errorResponse = errorResponse(joinPoint, returnValue);
if (errorResponse != null) {
System.out.format("problem detail: %s%n", errorResponse.getBody().getDetail());
}
}
@Nullable
private ErrorResponse errorResponse(JoinPoint ignored, Object returnValue) {
if (returnValue instanceof ErrorResponse errorResponse) {
return errorResponse;
} else if (returnValue instanceof ResponseEntity<?> responseEntity &&
responseEntity.getBody() instanceof ErrorResponse errorResponse) {
return errorResponse;
}
return null;
}
}
}
运行此测试会导致以下失败:
...
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'problemDetailsExceptionHandler' defined in class path resource [org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration$ProblemDetailsErrorHandlingConfiguration.class]: Could not generate CGLIB subclass of class org.springframework.boot.autoconfigure.web.servlet.ProblemDetailsExceptionHandler: Common causes of this problem include using a final class or a non-visible class
...
Caused by: org.springframework.aop.framework.AopConfigException: Could not generate CGLIB subclass of class org.springframework.boot.autoconfigure.web.servlet.ProblemDetailsExceptionHandler: Common causes of this problem include using a final class or a non-visible class
...
at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.createProxy(AbstractAutoProxyCreator.java:464) ~[spring-aop-6.0.4.jar:6.0.4]
at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.wrapIfNecessary(AbstractAutoProxyCreator.java:369) ~[spring-aop-6.0.4.jar:6.0.4]
...
Caused by: java.lang.IllegalArgumentException: Cannot subclass final class org.springframework.boot.autoconfigure.web.servlet.ProblemDetailsExceptionHandler
at org.springframework.cglib.proxy.Enhancer.generateClass(Enhancer.java:653) ~[spring-core-6.0.4.jar:6.0.4]
at org.springframework.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:26) ~[spring-core-6.0.4.jar:6.0.4]
...
笔记
ProblemTest
在 Spring Boot 2.x 上成功,并且在spring.mvc.problemdetails.enabled=false
3.x时成功。它仅在 3.x 上失败spring.mvc.problemdetails.enabled=true
。