项目中添加了一个公共请求日志打印过滤器
@Component
@WebFilter(filterName = "requestLogFilter",urlPatterns = {"/*"},
initParams = {@WebInitParam(name = "ignoredUrl", value = ".css;.js;.jpg;.png;.gif;.ico;.html"),
@WebInitParam(name = "filterPath", value = "/user/login#/user/registerUser")})
@Slf4j
public class RequestLogFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest servletRequest, @NotNull HttpServletResponse servletResponse,
@NotNull FilterChain filterChain) throws ServletException, IOException {
RequestLogWrapper requestWapper = null;
if (!servletRequest.getRequestURI().toLowerCase().contains("swagger")
&& !servletRequest.getRequestURI().toLowerCase().contains("upload")) {
//排除swagger、上传文件等接口
requestWapper = new RequestLogWrapper(servletRequest);
}
ResponseLogWrapper responseLogWrapper = new ResponseLogWrapper(servletResponse);
if (requestWapper == null) {
filterChain.doFilter(servletRequest, servletResponse);
} else {
filterChain.doFilter(requestWapper, responseLogWrapper);
}
//打印返回响应日志
String result = new String(responseLogWrapper.getResponseData());
ServletOutputStream outputStream = servletResponse.getOutputStream();
outputStream.write(result.getBytes());
outputStream.flush();
outputStream.close();
String queryStr = StrUtil.isEmpty(servletRequest.getQueryString()) ? "" : "?" + servletRequest.getQueryString();
log.info("=================过滤器====================请求路径: {}{} 响应报文: {}", servletRequest.getRequestURI(), queryStr, result);
}
@Override
public void destroy() {
logger.info(">>>> RequestLogFilter destroy <<<<");
}
}
@Slf4j
@Setter
public class RequestLogWrapper extends HttpServletRequestWrapper {
private String requestBody;
private static final String METHOD_GET = "GET";
private static final String METHOD_POST = "POST";
public RequestLogWrapper(HttpServletRequest request) {
super(request);
requestBody = getBodyString(request);
String queryStr = StrUtil.isEmpty(request.getQueryString()) ? "" : "?" + request.getQueryString();
if(METHOD_GET.equals(request.getMethod())){
log.info("=================过滤器====================【GET】请求路径: {}{} ", request.getRequestURI(), queryStr);
}else if (METHOD_POST.equals(request.getMethod())){
log.info("=================过滤器====================【POST】请求路径: {}{}, 请求报文: {}", request.getRequestURI(), queryStr, requestBody);
}
}
@Override
public ServletInputStream getInputStream() throws IOException {
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(requestBody.getBytes(StandardCharsets.UTF_8));
return new ServletInputStream() {
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
}
@Override
public int read() {
return byteArrayInputStream.read();
}
};
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(getInputStream()));
}
public String getBodyString(HttpServletRequest request) {
StringBuilder sb = new StringBuilder();
try(BufferedReader reader = new BufferedReader(new InputStreamReader(request.getInputStream(), StandardCharsets.UTF_8));) {
String line = "";
while ((line = reader.readLine()) != null) {
sb.append(line);
}
} catch (IOException e) {
e.printStackTrace();
}
return sb.toString();
}
}
@Slf4j
public class ResponseLogWrapper extends HttpServletResponseWrapper {
private ByteArrayOutputStream buffer = null;
private ServletOutputStream out = null;
private PrintWriter writer = null;
public ResponseLogWrapper(HttpServletResponse resp) throws IOException {
super(resp);
buffer = new ByteArrayOutputStream();
out = new WapperedOutputStream(buffer);
writer = new PrintWriter(new OutputStreamWriter(buffer, this.getCharacterEncoding()));
}
/** 重载父类获取outputstream的方法 */
@Override
public ServletOutputStream getOutputStream() throws IOException {
return out;
}
/** 重载父类获取writer的方法 */
@Override
public PrintWriter getWriter(){
return writer;
}
/** 重载父类获取flushBuffer的方法 */
@Override
public void flushBuffer() throws IOException {
if (out != null) {
out.flush();
}
if (writer != null) {
writer.flush();
}
}
@Override
public void reset() {
buffer.reset();
}
/** 将out、writer中的数据强制输出到WapperedResponse的buffer里面,否则取不到数据 */
public byte[] getResponseData() throws IOException {
flushBuffer();
return buffer.toByteArray();
}
/** 内部类,对ServletOutputStream进行包装 */
private static class WapperedOutputStream extends ServletOutputStream {
private ByteArrayOutputStream bos = null;
public WapperedOutputStream(ByteArrayOutputStream stream){
bos = stream;
}
@Override
public void write(int b) throws IOException {
bos.write(b);
}
@Override
public void write(byte[] b) throws IOException {
bos.write(b, 0, b.length);
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setWriteListener(WriteListener listener) {
}
}
}
项目启动的时候报错:
***************************
APPLICATION FAILED TO START
***************************
Description:
The bean 'requestLogFilter' could not be registered. A bean with that name has already been defined in file and overriding is disabled.
Action:
Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true
第一次我去掉了 Filter上的@Component,项目可以启动了,但是其他使用这个拦截器无法生效
第二次我恢复@Component,然后把@WefFilter中的filterName改成
@WebFilter(filterName = "requestLog") 然后项目也可以启动,但是过滤器生效两次
第三次,我查看项目差异,发现过滤器生效两次的项目使用了一个注解 @ServletComponentScan ,查询得知此注解作用:在SpringBootApplication上使用@ServletComponentScan注解后,Servlet、Filter、Listener可以直接通过@WebServlet、@WebFilter、@WebListener注解自动注册,无需其他代码。
所以知道了原因,这个过滤器在spring容器注册了两次,所以才会出现同名冲突的报错。
后面去掉注解,启动无问题