目前打印 springboot 的请求日志,一般使用拦截器或者AOP
这里提供一种更为通用且简洁的实现方案
拦截器的缺点
ServletRequest的InputStream只能读取一次,还需要配合可重复读取的RquestWrapper
AOP 的缺点
需要为所有的Controller类创建代理对象,对项目启动速度和内存占用有轻微影响。
配置spring的切面,一是使用注解,那就需要在所有方法上添加自定义的注解;二是按照包名配置切点,由于多个项目的包未必一致,使得这种方案在实现通用性上较为困难。
或许是更好的方案
在springMVC中,所有Servlet请求都会通过
org.springframework.web.method.support.InvocableHandlerMethod#doInvoke
来反射调用RequestMapping对应的方法,那只要能在这个方法的前后打印方法的输入和输出,就可以实现打印接口请求日志的目标。
而org.springframework.web.method.support.InvocableHandlerMethod
在DispathcerServlet
中是通过RequestMappingHandlerAdapter
来封装的,正巧spring框架提供了一个扩展点
org.springframework.boot.autoconfigure.web.servlet.WebMvcRegistrations
可以让使用者自定义RequestMappingHandlerAdapter
的实现类。
这种方案更具备通用性,可以被打包进公司或项目的通用代码中,并且相较于代理Contrlller对象的方式,也能小幅度提升项目的启动耗时和内存占用。
废话不多说,直接上一份精简版的示例代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
| import org.springframework.boot.autoconfigure.web.servlet.WebMvcRegistrations; import org.springframework.core.io.InputStreamSource; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter; import org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod;
import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest;
@Component public class ControllerLogger implements WebMvcRegistrations {
@Override public RequestMappingHandlerAdapter getRequestMappingHandlerAdapter() { return new RequestMappingHandlerAdapter() {
@Override protected ServletInvocableHandlerMethod createInvocableHandlerMethod(final HandlerMethod handlerMethod) { return new LoggingServletInvocableHandlerMethod(handlerMethod); } }; }
private static class LoggingServletInvocableHandlerMethod extends ServletInvocableHandlerMethod {
private LoggingServletInvocableHandlerMethod(final HandlerMethod handlerMethod) { super(handlerMethod); }
@Override protected Object doInvoke(final Object... arguments) throws Exception { final ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); final HttpServletRequest request = attributes.getRequest(); final Object dto = ControllerLogger.determineRequestDTO(arguments); try { final Object result = super.doInvoke(arguments); return result; } catch (Exception e) { throw e; } } }
private static Object determineRequestDTO(final Object... arguments) { if (arguments == null) { return null; } for (final Object argument : arguments) { if (argument instanceof ServletRequest || argument instanceof ServletResponse || argument instanceof InputStreamSource) { continue; } return argument; } return null; } }
|