拦截器
拦截器(Interceptor)同 Filter 过滤器一样,它俩都是面向切面编程——AOP 的具体实现(AOP切面编程只是一种编程思想而已)。
你可以使用 Interceptor 来执行某些任务,例如在 Controller 处理请求之前编写日志,添加或更新配置…
在 Spring中,当请求发送到 Controller 时,在被Controller处理之前,它必须经过 Interceptors(0或多个)。
Spring Interceptor是一个非常类似于Servlet Filter 的概念 。
自定义拦截器
具体方法:
自定义类实现
org.springframework.web.servlet.HandlerInterceptor
接口或继承org.springframework.web.servlet.handler.HandlerInterceptorAdapter
类方法执行顺序:
- 如果拦截成功:prehandler 之后不再执行
- 如果拦截失败:prehandler -> controller -> posthandler -> afterCompletion
@Slf4j
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("PreHandler");
log.info("PreHandler");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("PostHandle");
log.info("PostHandler");
return;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("After");
log.info("After");
HandlerInterceptor.super.afterCompletion(request,response,handler,ex);
}
}
配置注册拦截器
对于每个拦截器,要配置其拦截的对象,和拦截优先级。
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 配置接口路径
registry.addInterceptor(new MyInterceptor()).addPathPatterns("/*/testHandler").order(0);
}
}
如何在拦截器中注册普通Bean
今天我有个想法,想将一个普通Bean注册到拦截器中,但是发现怎么都拿不到这个Bean,后来才知道是拦截器在Context初始化之前就注册了。
因为拦截器是在上下文容器 Spring Context 初始化之前执行,所以没有办法直接在拦截器中注入普通Bean。
解决方法
// 拦截器,这类我想注入httpUtils和showEntity
public class Interceptor implements HandlerInterceptor {
/**
* 配置拦截器,拦截相应的路径,转发请求
*/
@Resource
HttpUtils httpUtils;
@Resource
ShowEntity showEntity;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return true;
}
}
将拦截器也通过当作普通Bean,使用@Bean注入,而不是在配置中直接new一个拦截器对象,
// 一般来说会这样配拦截器,但是这样在context初始化之前就注入拦截器了,当然拿不到httpUtils和showEntity。
@Configuration
public class Config implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new Interceptor()).addPathPatterns("/testa/needToProcess");
}
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedHeaders("*");
}
}
正确做法,这样就可以在Interceptor中注入httpUtils和showEntity了。
@Configuration
public class Config implements WebMvcConfigurer {
@Bean
public Interceptor interceptor(){
return new Interceptor();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(interceptor()).addPathPatterns("/testa/needToProcess");
}
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedHeaders("*");
}
}
拦截器拦截请求后,拿到请求体中的参数,并传递给请求的原方法
拦截器中,一个
Request
的getReader()
和getInputStream()
只能调用一次。如果在拦截器中读了请求体,那么到Controller
就取不到原Request
中的请求体数据了。解决方法:
重写一个
RequestWrapper
,将从request
中读取的body
保存下来,再通过重写Filter
传递RequestWrapper
给Congtroller
。
public class RequestWrapper extends HttpServletRequestWrapper {
private final String body;
public RequestWrapper(HttpServletRequest request){
super(request);
StringBuilder stringBuilder = new StringBuilder();
BufferedReader bufferedReader =null;
InputStream inputStream = null;
try {
inputStream = request.getInputStream();
if(inputStream != null){
bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
char[] charBuffer = new char[128];
int byteRead = -1;
while((byteRead = bufferedReader.read(charBuffer)) > 0){
stringBuilder.append(charBuffer,0,byteRead);
}
}else {
stringBuilder.append("");
}
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if(inputStream != null){
try{
inputStream.close();
}catch(IOException e){
e.printStackTrace();
}
}
if(bufferedReader != null){
try{
bufferedReader.close();
}catch(IOException e){
e.printStackTrace();
}
}
}
body = stringBuilder.toString();
}
@Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes());
ServletInputStream servletInputStream = new ServletInputStream() {
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
}
@Override
public int read() throws IOException {
return byteArrayInputStream.read();
}
};
return servletInputStream;
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(this.getInputStream()));
}
public String getBody(){
return this.body;
}
}
public class RepeatedlyReadFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
ServletRequest requestWrapper = null;
if(servletRequest instanceof HttpServletRequest){
requestWrapper = new RequestWrapper((HttpServletRequest) servletRequest);
}
if(requestWrapper == null){
filterChain.doFilter(servletRequest,servletResponse);
}else {
filterChain.doFilter(requestWrapper,servletResponse);
}
}
@Override
public void destroy() {
}
}
修改配置类,注册
Filter
@Configuration
public class WebConfig implements WebMvcConfigurer {
/**
* 拦截器
*/
@Bean
public Interceptor interceptor(){
return new Interceptor();
}
/**
* 过滤器
*/
@Bean
public FilterRegistrationBean<RepeatedlyReadFilter> repeatedlyReadFilter(){
FilterRegistrationBean<RepeatedlyReadFilter> registrationBean = new FilterRegistrationBean<>();
RepeatedlyReadFilter repeatedlyReadFilter = new RepeatedlyReadFilter();
registrationBean.setFilter(repeatedlyReadFilter);
registrationBean.addUrlPatterns("/*");
return registrationBean;
}
/**
* 拦截器配置
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(interceptor()).addPathPatterns("/testa/needToProcess");
}
/**
* 跨域配置
*/
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedHeaders("*");
}
}