SpringBoot拦截机制使⽤详解
版权声明:本⽂为博主原创⽂章,未经博主允许不得转载。 blog.csdn/qq_37142346/article/details/79890209
引⼊场景:当我们在某些情况下需要对客户端发送来的请求进⾏拦截分析的时候,就需要⽤到拦截机制,⽐如,我们需要对⼀个请求进⾏计时,⼜或者需要知道当前请求需要进⼊哪个控制器,哪⼀个⽅法,该请求的参数是什么等等场景下都需要⽤到拦截机制来处理。下⾯,我们来讲解⼀下SpringBoot的⼏种拦截⽅式以及如何使⽤它们来处理⼀定的场景需求。
⼀、SpringBoot的拦截机制
主要有下⾯三种拦截机制:
过滤器(filter)
(interceptor)
切⽚(aspect)
上⾯的这三种拦截机制有什么区别呢?
过滤器可以拦截发送请求的状态码以及信息,除了可以拦截filter可以拦截的,还可以得到当前请求进⼊了哪⼀个controller,以及映射到哪⼀个⽅法,⽽aspect呢,它具有上⾯的所有功能外,还可以得到当前请求的参数的值。
这三种的顺序是怎么样的呢?
如上图所⽰,当⼀个请求发送来的时候,filter在最外层,也最先拦截到请求,接下来就是interceptor,依次是ControllerAdvice(处理controller层异常)、aspect,最后才进⼊controller层去处理请求。相应的,当controller内部发⽣错误,抛出异常的时候,aspect最先接收到该异常,如果不对抛出的异常继续处理继续往外抛的话依次会抛到ControllerAdvice、interceptor、filter。
在简单介绍完以上三种拦截机制后,下⾯来看看如何使⽤这三种拦截机制。
⼆、SpringBoot的拦截机制的使⽤
1.过滤器(Filter)
⾸先,我们需要来⾃⼰定义⼀个MyFilter类,然后需要实现Filter接⼝:
package cn.shinelon.filter;
import java.io.IOException;
import java.util.Date;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.springframework.stereotype.Component;
/**
* @author Shinelon
*
*/
@Component
public class MyFilter implements Filter {
/* (non-Javadoc)
* @see javax.servlet.Filter#destroy()
*/
@Override
public void destroy() {
System.out.println("time is destory");
}
/* (non-Javadoc)
* @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain)
*/
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
throws IOException, ServletException {
System.out.println("the filter is begin");
long start=new Date().getTime();
chain.doFilter(req, resp);
System.out.println("耗时为:"+(new Date().getTime()-start)+"ms");
System.out.println("the filter is end");
}
/* (non-Javadoc)
* @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
*/
@Override
public void init(FilterConfig arg0) throws ServletException {
System.out.println("time is init");
springboot aop}
}
上⾯的filter类会重写三个⽅法,这三个⽅法见名知意,最核⼼的⽅法是doFilter⽅法,该⽅法内部会调⽤chain.doFilter(req, resp);⽅法来调⽤发送来的请求,在这个⽅法⾥我们就可以拿到我们需要的⼀系列拦截信息。因此,在定义好该⽅法后我们就可以启动项⽬,会打印请求的耗时。
但是,有时候我们需要调⽤第三⽅框架的的时候,就不能像这个过滤器⼀样这样使⽤,因为这是j2EE中⾃带的过滤器。在SpringMvc中,我们可以在l⽂件中注⼊我们要 引⼊第三⽅框架的过滤器,在SpringBoot中,我们需要声明⼀个配置类,这个类作⽤相当于之前的xml⽂件来管理bean。
下⾯,我们来添加⼀个配置类,不过上⾯filter中需要去掉@component注解:package fig;
import java.util.ArrayList;
import java.util.List;
import org.aopalliance.intercept.Interceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import t.annotation.Bean;
import t.annotation.Configuration;
import org.springframework.fig.annotation.InterceptorRegistry;
import org.springframework.fig.annotation.WebMvcConfigurerAdapter;
import cn.shinelon.filter.MyFilter;
import cn.shinelon.interceptor.TimeInteceptor;
/**
* @author Shinelon
*
*/
@Configuration
public class FilterConfig extends WebMvcConfigurerAdapter{
@Bean
public FilterRegistrationBean myFilter() {
FilterRegistrationBean filterRegistrationBean=new FilterRegistrationBean();
MyFilter myFilter=new MyFilter();
filterRegistrationBean.setFilter(myFilter);
List<String> urls=new ArrayList<String>();
urls.add("/*"); //过滤所有请求
filterRegistrationBean.setUrlPatterns(urls);
return filterRegistrationBean;
}
}
与上⾯不同的是,这⾥我们可以设置过滤的请求的格式,这⾥过滤所有请求。
2.interceptor
如何使⽤呢?与上⾯类似,我们还是需要⾃⼰来编写:
这个需要实现HandlerInterceptor接⼝,这个接⼝声明了三个⽅法,作⽤看上⾯注释。
在这⾥需要注意的是preHandle⽅法会返回⼀个Boolean值,这个值关系这的执⾏,如果返回true,就继续执⾏下⾯的⽅法,如果返回false则不会执⾏后⾯的业务。我们可以深⼊源码来看看。
我们进⼊到spring框架的DispatcherServlet类中有⼀个doDispatch⽅法,这个⽅法中有下⾯⼀段代码:
⾸先,它会先判断是否通过了PreHandle⽅法,如果通过就会真正的调⽤handler,将request,response等请求信息通过handle⽅法封package cn.shinelon.interceptor;
import java.util.Date;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.hod.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
@Component
public class TimeInteceptor implements HandlerInterceptor {
//⽆论controller 中是否抛出异常,都会调⽤该⽅法
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object obj, Exception ex)
throws Exception {
System.out.println("afterCompletion");
long start=new Date().getTime();
System.out.println("最后耗时为:"+(new Date().getTime()-start));
System.out.println("ex is"+ex);
}
//如果controller 中抛出异常,则该⽅法不会被调⽤
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object obj, ModelAndView view)
throws Exception {
// ModelMap map=new ModelMap();
// map.addAttribute("name", "shinelon");
System.out.println("postHandle");
long start=(long) Attribute("start");
System.out.println("消耗的时间为:"+(new Date().getTime()-start));
}
//最先执⾏该⽅法
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object obj) throws Exception {
System.out.println("preHandle");
System.out.println(((HandlerMethod)obj).getBean().getClass().getName());
System.out.println(((HandlerMethod)obj).getMethod().getName());
request.setAttribute("start", new Date().getTime());
System.out.println("开始时间:"+new Date().getTime());
return true;
}
}
装起来。因此,我们需要⼩⼼处理PreHandler⽅法中业务以及返回值。
与filter不同的是,它在创建interceptor之后,不能⽣效,我们需要创建⼀个配置类将该注⼊到spring容器中。这个配置类继承了WebMvcConfigurerAdapter类。
package fig;
import java.util.ArrayList;
import java.util.List;
import org.aopalliance.intercept.Interceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import t.annotation.Bean;
import t.annotation.Configuration;
import org.springframework.fig.annotation.InterceptorRegistry;
import org.springframework.fig.annotation.WebMvcConfigurerAdapter;
import cn.shinelon.filter.MyFilter;
import cn.shinelon.interceptor.TimeInteceptor;
/**
* @author Shinelon
*
*/
@Configuration
public class FilterConfig extends WebMvcConfigurerAdapter{
@Autowired
public TimeInteceptor timeInteceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(timeInteceptor);
}
}
这样,我们的就会⽣效,我们可以在中得到发送来的请求进⼊哪⼀个控制器以及⽅法。
3.切⽚(aspect)
最后,我们来看看切⽚的使⽤,对于切⽚,相信使⽤spring的⼈都⾮常熟悉,spring的核⼼功能AOP,⾯向切⾯编程,这⾥,我们不详解aop,读者可以去查看相关资料。下⾯我们来看看使⽤切⽚来实现拦截机制。
⾸先,需要引⼊相关依赖:
<!-- mvnrepository/artifact/org.springframework.boot/spring-boot-starter-aop -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
下⾯我们来创建切⾯:
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论