Java Spring中Servlet过滤器的内部工作

Java Spring中Servlet过滤器的内部工作,java,spring,spring-boot,servlet-filters,Java,Spring,Spring Boot,Servlet Filters,我来自Node ExpressJS,因此我熟悉中间件的概念。在学习Spring的过程中,我了解到一个名为Filter的组件,它的作用与Express中的中间件非常相似,但有一些不同 因此,我试图了解过滤器和过滤器链在春季是如何工作的 我有以下代码: Filter1.java @Component @Order(1) public class Filter1 implements Filter { ..... ..... @Override public voi

我来自Node ExpressJS,因此我熟悉中间件的概念。在学习Spring的过程中,我了解到一个名为
Filter
的组件,它的作用与Express中的中间件非常相似,但有一些不同

因此,我试图了解
过滤器和
过滤器链在春季是如何工作的

我有以下代码:

Filter1.java

@Component
@Order(1)
public class Filter1 implements Filter {

    .....
    .....

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {

        LOGGER.info("############# Invoking Filter1 ############");
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse resp = (HttpServletResponse) response;

        LOGGER.info("************ Moving on to next Filter");
        LOGGER.info("Adding new Attribute");
        req.setAttribute("Custom_Attribute_1", "TEST***TEST***TEST");
        chain.doFilter(request, response);
        resp.addHeader("1st Header", "1ST"); // Custom header that never shows up

        LOGGER.info("+++++++++ GOING BACK FROM Filter1 +++++++++");

    }

}
@Component
@Order(2)
public class Filter2 implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        LOGGER.info("############# Invoking Filter2 ####################");
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse resp = (HttpServletResponse) response;

        req.setAttribute("Filter2 Attribute", "2nd ORDER");
        resp.addHeader("2nd Header", "2ND"); //Custom header that actually shows up
        chain.doFilter(request, response);      
        LOGGER.info("+++++++++++ GOING BACK FROM Filter2 ++++++++++");
    }

}
@RestController
public class Controller {

   @GetMapping("/")
   public ResponseEntity<Object> createResource(HttpServletRequest req) {
     return new ResponseEntity<Object>("Resource Created",HttpStatus.OK);
   }
}
Filter2.java

@Component
@Order(1)
public class Filter1 implements Filter {

    .....
    .....

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {

        LOGGER.info("############# Invoking Filter1 ############");
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse resp = (HttpServletResponse) response;

        LOGGER.info("************ Moving on to next Filter");
        LOGGER.info("Adding new Attribute");
        req.setAttribute("Custom_Attribute_1", "TEST***TEST***TEST");
        chain.doFilter(request, response);
        resp.addHeader("1st Header", "1ST"); // Custom header that never shows up

        LOGGER.info("+++++++++ GOING BACK FROM Filter1 +++++++++");

    }

}
@Component
@Order(2)
public class Filter2 implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        LOGGER.info("############# Invoking Filter2 ####################");
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse resp = (HttpServletResponse) response;

        req.setAttribute("Filter2 Attribute", "2nd ORDER");
        resp.addHeader("2nd Header", "2ND"); //Custom header that actually shows up
        chain.doFilter(request, response);      
        LOGGER.info("+++++++++++ GOING BACK FROM Filter2 ++++++++++");
    }

}
@RestController
public class Controller {

   @GetMapping("/")
   public ResponseEntity<Object> createResource(HttpServletRequest req) {
     return new ResponseEntity<Object>("Resource Created",HttpStatus.OK);
   }
}
Controller.java

@Component
@Order(1)
public class Filter1 implements Filter {

    .....
    .....

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {

        LOGGER.info("############# Invoking Filter1 ############");
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse resp = (HttpServletResponse) response;

        LOGGER.info("************ Moving on to next Filter");
        LOGGER.info("Adding new Attribute");
        req.setAttribute("Custom_Attribute_1", "TEST***TEST***TEST");
        chain.doFilter(request, response);
        resp.addHeader("1st Header", "1ST"); // Custom header that never shows up

        LOGGER.info("+++++++++ GOING BACK FROM Filter1 +++++++++");

    }

}
@Component
@Order(2)
public class Filter2 implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        LOGGER.info("############# Invoking Filter2 ####################");
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse resp = (HttpServletResponse) response;

        req.setAttribute("Filter2 Attribute", "2nd ORDER");
        resp.addHeader("2nd Header", "2ND"); //Custom header that actually shows up
        chain.doFilter(request, response);      
        LOGGER.info("+++++++++++ GOING BACK FROM Filter2 ++++++++++");
    }

}
@RestController
public class Controller {

   @GetMapping("/")
   public ResponseEntity<Object> createResource(HttpServletRequest req) {
     return new ResponseEntity<Object>("Resource Created",HttpStatus.OK);
   }
}
调用
chain.doFilter(请求、响应)
与此有关吗?调用
FilterChain
doFilter
后,似乎无法修改
Filter1
类中的
response
对象

我想了解的是:

  • 如果需要调用
    FilterChain.doFilter
    来将
    请求
    对象传播到下一个筛选器,并最终传播到控制器,那么对
    链.doFilter
    的调用返回后,是否应允许修改
    响应
    对象?它究竟是如何在内部工作的?调用如何将所有过滤器向下传播到控制器,然后返回到第一个过滤器

  • 另外,如果Filter1希望在响应从Filter2返回后查看响应主体并可能对其进行修改,它将如何执行

  • 基本上,
    doFilter()
    将请求和响应对象发送到FilterChain中的下一个筛选器,如中所述

    在第一个示例中,在添加“第一个头”之前,您将调用到tha链中的下一个过滤器。这就是为什么你没有得到“第一个标题”在第一位。此请求将转到控制器层,然后由您的控制器进行评估。当控制器处理完对象后,响应开始填充回过滤器

    所以你的代码是这样工作的

    arrived Filter1 -> Filter 2 > add "2st Header" > .. > Controller > Controller runs and prepares a response object.
    
    Controller Response > .... > Filter 2 > Filter 1 > add "1st Header" > ...
    
    因此,当请求到达控制器时,控制器在请求上下文中永远不会有“第一个头”

    另外,如果Filter1希望在响应从Filter2返回后看到响应体并可能对其进行修改,它将如何进行修改


    我没有试过,但是你应该看看,它看起来像你想要实现的。

    这里没有魔法-对
    doFilter
    的调用被阻塞了。当它返回(正常或异常)时,下游过滤器和请求处理器本身完成。与其说它是一条链,不如说它更像是俄罗斯的嵌套玩偶。你的代码不起作用的原因正是与此相关的-一旦
    doFilter
    返回主请求处理器已完成-它很可能刷新了流并发送了内容。一旦发送了头,就不能再发送更多的头-TCP上没有通过引用传递!如果要在之后添加标题,则需要缓冲整个响应—这可能非常昂贵,甚至是不可能的。