Filter基于函数回调,依赖于servlet容器
,可以对几乎所有请求进行过滤是链式处理
的。过滤顺序按照web.xml中配置的顺序,但是缺点是一个过滤器实例只能在容器初始化时调用一次
。使用过滤器的目的,是用来做一些过滤操作,获取我们想要获取的数据。
在过滤器中修改字符编码(CharacterEncodingFilter)、在过滤器中修改HttpServletRequest的一些参数(XSSFilter(自定义过滤器)),如:过滤低俗文字、危险字符等。
@Component
@WebFilter(urlPatterns = { "/**" }, filterName = "testFilter")
public class TestFilter implements Filter{
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
System.out.println("test filter");
chain.doFilter(request, response);
}
@Override
public void init(FilterConfig arg0) throws ServletException {
}
}
基于Java的反射机制,属于面向切面编程(AOP)的一种运用。依赖于web框架,如
SpringMVC框架。在service或者一个方法前后,调用一个方法,比如动态代理就是拦截器的简单实现。
由于拦截器是基于web框架的调用,因此可以使用Spring的依赖注入(DI)进行一些业务操作,同时一个拦截器实例在一个controller生命周期之内可以多次调用。但是缺点是只能对controller请求进行拦截,对其他的一些比如直接访问静态资源的请求则没办法进行拦截处理。
在调用方法前后做其它业务逻辑的操作,甚至在抛出异常的时候做业务逻辑的操作。
public class MyInterceptor implements HandlerInterceptor{
//请求处理之前进行调用(Controller方法调用之前)
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
System.out.printf("MyInterceptor preHandle");
HandlerMethod handlerMethod = (HandlerMethod) o;
Method method = handlerMethod.getMethod();
if (method.getAnnotation(interceptorLog.class) != null) {
return true;
}else{
return false;
}
}
//请求处理之后进行调用(Controller方法调用之后)
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
System.out.println("MyInterceptor postHandle");
}
//在整个请求结束之后被调用(主要是用于进行资源清理工作)
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
System.out.println("MyInterceptor afterCompletion");
}
}
@Configuration
public class MyConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
//注册TestInterceptor拦截器
registry.addInterceptor(new MyInterceptor()) //指定拦截器类
.addPathPatterns("/**"); //指定该类拦截的url
}
}
只能拦截Spring管理Bean的访问(业务层Service)。
实际开发中,AOP常和事务结合,Spring的事务管理。声明式事务管理(切面)
AOP操作可以对操作进行横向的拦截,最大的优势在于可以获取执行方法的参数( ProceedingJoinPoint.getArgs() ),对方法进行统一的处理
。
可以自定义切入的点,有方法的参数,但是拿不到http请求,可以通过其他方式如RequestContextHolder获得。
日志记录,使用过滤器不能细致地划分模块,此时应该考虑拦截器。然而拦截器也是依据URL做规则匹配,因此相对来说不够细致,因此我们会考虑到使用AOP实现,AOP可以针对代码的方法级别做拦截,很适合日志功能。
常见使用日志,事务,请求参数安全验证等。
@Component
@Aspect
public class HttpAspect {
@Pointcut("execution(* com.test.controller..*.*(..)) && @annotation(spring.annotation.aopLog)")
public void log(){
}
@Around("log()")
public void aroundLogCalls(JoinPoint joinPoint) throws Throwable {
System.out.println("aop round");
}
@Before("log()")
public void doBefore(JoinPoint joinPoint){
ServletRequestAttributes attributes= (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
HttpServletRequest request =attributes.getRequest();
System.out.println("aop doBefore");
}
@After("log()")
public void doAfter(){
System.out.println("aop doAfter");
}
@AfterThrowing("log()")
public void cathInfo(){
System.out.println("异常信息");
}
}
三者功能类似,但各有优势。
从过滤器->拦截器->AOP,拦截规则越来越细致。
执行顺序依次是过滤器、拦截器、切面。一般情况下数据被过滤的时机越早对服务的性能影响越小,因此我们在编写相对比较公用的代码时,优先考虑过滤器,然后是拦截器,最后是aop。
报错处理顺序切面、拦截器、过滤器。