Java Spring中Servlet过滤器的内部工作
我来自Node ExpressJS,因此我熟悉中间件的概念。在学习Spring的过程中,我了解到一个名为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
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上没有通过引用传递!如果要在之后添加标题,则需要缓冲整个响应—这可能非常昂贵,甚至是不可能的。