@ServletComponentScan导致@Component和@WebFilter冲突

文章讲述了在项目中添加公共请求日志过滤器时遇到的问题,包括初始报错、解决尝试(如更改filterName和去掉@ServletComponentScan注解),以及最终确定冲突原因并给出解决方案的过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

项目中添加了一个公共请求日志打印过滤器

@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容器注册了两次,所以才会出现同名冲突的报错。

后面去掉注解,启动无问题

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值