
SpringBoot 项目中Filter注意事项
问题一:设置完filter之后 发现莫名其妙的bug:过滤器在controller之前执行没问题,之后执行就不行。
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
//先进接口
filterChain.doFilter(request, response);
RequestWrapper requestWrapper = new RequestWrapper(request);
String bodyString = requestWrapper.getBodyString();
System.out.println(bodyString);
JSONObject jsonObject = JSONUtil.parseObj(bodyString);
String account = jsonObject.getStr("account");
userService.lambdaUpdate().eq(User::getAccount, account).setSql("count = count + 1").update();
}
报错如下:
2025-02-21T16:59:22.532+08:00 ERROR 25856 --- [nio-8080-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception
cn.hutool.json.JSONException: A JSONObject text must begin with '{' at 1 [character 2 line 1]
at cn.hutool.json.JSONTokener.syntaxError(JSONTokener.java:413) ~[hutool-all-5.8.16.jar:5.8.16]
at cn.hutool.json.JSONParser.parseTo(JSONParser.java:48) ~[hutool-all-5.8.16.jar:5.8.16]
at cn.hutool.json.ObjectMapper.mapFromTokener(ObjectMapper.java:247) ~[hutool-all-5.8.16.jar:5.8.16]
at cn.hutool.json.ObjectMapper.mapFromStr(ObjectMapper.java:223) ~[hutool-all-5.8.16.jar:5.8.16]
at cn.hutool.json.ObjectMapper.map(ObjectMapper.java:98) ~[hutool-all-5.8.16.jar:5.8.16]
at cn.hutool.json.JSONObject.<init>(JSONObject.java:210) ~[hutool-all-5.8.16.jar:5.8.16]
at cn.hutool.json.JSONObject.<init>(JSONObject.java:187) ~[hutool-all-5.8.16.jar:5.8.16]
at cn.hutool.json.JSONObject.<init>(JSONObject.java:142) ~[hutool-all-5.8.16.jar:5.8.16]
at cn.hutool.json.JSONObject.<init>(JSONObject.java:125) ~[hutool-all-5.8.16.jar:5.8.16]
at cn.hutool.json.JSONUtil.parseObj(JSONUtil.java:88) ~[hutool-all-5.8.16.jar:5.8.16]
at com.base.app.core.filter.LoginFilter.doFilter(LoginFilter.java:46) ~[classes/:na]
........
可以发现,并无执行打印语句,然后我们把它移到后面,先执行filter:
{
"account": "admin",
"password": "123456"
}
2025-02-21T17:17:00.984+08:00 ERROR 29064 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception
可以发现,执行打印语句,说明request拿到json数据了,但是controller里没数据了,也就是说 给完之后,就不能给下一层了(具体原因未知,我是彩笔)
public class RequestWrapper extends HttpServletRequestWrapper {
private final byte[] body;
public RequestWrapper(HttpServletRequest request) throws IOException {
super(request);
//保存一份InputStream,将其转换为字节数组
body = StreamUtils.copyToByteArray(request.getInputStream());
}
//转换成String
public String getBodyString(){
return new String(body, StandardCharsets.UTF_8);
}
@Override
public BufferedReader getReader() {
return new BufferedReader(new InputStreamReader(getInputStream()));
}
//把保存好的InputStream,传下去
@Override
public ServletInputStream getInputStream() {
final ByteArrayInputStream basis = new ByteArrayInputStream(body);
return new ServletInputStream() {
@Override
public int read() {
return basis.read();
}
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
}
};
}
}
通过保存一份流,就可实现在过滤器中能拿到JSON参数,同时Controller也不会丢失参数
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
RequestWrapper requestWrapper = new RequestWrapper(request);
//先进接口
filterChain.doFilter(requestWrapper, response);
String bodyString = requestWrapper.getBodyString();
System.out.println(bodyString);
JSONObject jsonObject = JSONUtil.parseObj(bodyString);
String account = jsonObject.getStr("account");
User eq = userService.lambdaQuery().eq(User::getAccount, account).one();
eq.setCount(eq.getCount()+1);
userService.updateById(eq);
}
ok 解决
问题二:无法注入问题问题
这个问题我问了ChatGPT:
过滤器的初始化顺序:
Filter
是由 Servlet 容器在请求处理之前初始化的,通常它是在 Spring 的容器启动之前创建和调用的。因此,Spring 的依赖注入(例如通过@Resource
注解)可能会失败,因为过滤器的生命周期并不完全与 Spring 的上下文同步。Filter 的生命周期管理: Spring 管理的普通 bean(如服务层、Controller)是通过 IoC 容器注入的,但是
Filter
是由容器管理的特殊对象,并且通常是由FilterRegistrationBean
注册的,这使得它不一定按照常规的方式进行依赖注入。
他给的解决方案:
使用FilterRegistrationBean
注册过滤器时,注入依赖: 如果你想确保Filter
中的依赖能够正确注入,你可以通过FilterRegistrationBean
的构造函数传递依赖(像你前面提到的userRepository
)。这种方式可以确保依赖注入在注册过滤器时就能生效。手动注入依赖: 如果非要在
Filter
中使用@Resource
,你可以手动调用Spring
的上下文来获取需要的服务实例。例如,通过SpringContextUtil
类来获取ApplicationContext
,然后从中获取需要的服务。
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean<LoginFilter> loggingFilter(UserService userService) {
FilterRegistrationBean<LoginFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new LoginFilter(userService)); //在这里 把这个service和filter的bean一起注入进去
registrationBean.setOrder(1);
registrationBean.addUrlPatterns("/api/*"); // 配置拦截路径
return registrationBean;
}
}
private final UserService userService;
public LoginFilter(UserService userService) {
this.userService = userService;
}
然后用的时候用构造器注入即可~