如何在AOP Around通知中处理可调用对象?



我有一个控制器在我的Spring Boot应用程序。在我的控制器中,我有一个端点,如果时间过长,我需要超时调用。为此,我从该方法返回一个Callable,并在application.yml中包含配置spring.mvc.async.request-timeout。这似乎很适合我们的目的。

我在这个应用程序中还有一个Aspect类,它包含一个方法,每当我的控制器中的方法被调用时,这个方法就会被触发。此方法的目的是记录细节,例如端点花费的时间量、响应代码是什么等等。当该方法的响应不是Callable(即。aResponseEntity),因为我可以毫无问题地从返回类型获得响应信息。但是,当方法返回Callable而不从方面类调用((Callable) ProceedingJoinPoint.proceed()).call()时,我无法获得此响应信息。这使得API调用时间更长,我相信这是因为它调用了两次call()。是否有任何方法可以获得响应信息,而不必在Aspect类中使用call()?

这是一个简单的例子,我已经在我的方面类:

@Around("...")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {

Object result = joinPoint.proceed();
if (!(result instanceof Callable<?>)) {
// Do logging using result, which is a ResponseEntity...
} else {
Object callableResult = ((Callable<?>) result).call();
// Do logging using callableResult, which is a ResponseEntity...
}
return result;
}

谢谢。

我在工作中遇到了同样的情况,下面似乎解决了它:异步Spring MVC控制器方法后的日志响应体

注意:尝试在用@ControllerAdviceimplements ResponseBodyAdvice注释的类中记录异步响应。这应该捕获真实的响应,而不是可调用的。

你可以有一个类,用@Aspects来记录请求,用@ControllerAdvice来记录响应。

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.concurrent.atomic.AtomicLong;

@Aspect
@ControllerAdvice
public class LoggingAdvice implements ResponseBodyAdvice {
private static final Logger LOGGER = LoggerFactory.getLogger(LoggingAdvice.class);
private static final AtomicLong ID = new AtomicLong();
@Before("within(com.example.demo.controller..*)")
public void endpointBefore(JoinPoint p) {
LOGGER.info(p.getTarget().getClass().getSimpleName() + " " + p.getSignature().getName() + " START");
Object[] signatureArgs = p.getArgs();
ObjectMapper mapper = new ObjectMapper();
mapper.enable(SerializationFeature.INDENT_OUTPUT);
try {
if (signatureArgs != null && signatureArgs.length > 0) {
LOGGER.info("nRequest object: n" + mapper.writeValueAsString(signatureArgs[0]));
} else {
LOGGER.info("request object is empty");
}
} catch (JsonProcessingException e) {
}
}
@Override
public boolean supports(MethodParameter returnType, Class converterType) {
return true;
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
long id = ID.incrementAndGet();
ServletServerHttpResponse responseToUse = (ServletServerHttpResponse) response;
HttpMessageConverter httpMessageConverter;
LoggingHttpOutboundMessageWrapper httpOutputMessage = new LoggingHttpOutboundMessageWrapper();
try {
httpMessageConverter = (HttpMessageConverter) selectedConverterType.newInstance();
httpMessageConverter.write(body, selectedContentType, httpOutputMessage);
LOGGER.info("response {}, {}, {}, {}, {}", id, responseToUse.getServletResponse().getStatus(), responseToUse.getServletResponse().getContentType(),
responseToUse.getHeaders(), httpOutputMessage.getResponseBodyInString());
} catch (InstantiationException | IllegalAccessException | IOException e) {
e.printStackTrace();
}
return body;
}
private static final class LoggingHttpOutboundMessageWrapper implements HttpOutputMessage {
private HttpHeaders httpHeaders = new HttpHeaders();
private ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
@Override
public OutputStream getBody() throws IOException {
return byteArrayOutputStream;
}
@Override
public HttpHeaders getHeaders() {
return httpHeaders;
}
public String getResponseBodyInString() {
return new String(byteArrayOutputStream.toByteArray());
}
}
}

最新更新