浅谈过滤器与拦截器与切面

过滤器(Filter)

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 {
		
	}
}

拦截器(Interceptor)

基于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
    }
}

面向切面(AOP)

只能拦截Spring管理Bean的访问(业务层Service)。实际开发中,AOP常和事务结合,Spring的事务管理。声明式事务管理(切面)
AOP操作可以对操作进行横向的拦截,最大的优势在于可以获取执行方法的参数( ProceedingJoinPoint.getArgs() ),对方法进行统一的处理

可以自定义切入的点,有方法的参数,但是拿不到http请求,可以通过其他方式如RequestContextHolder获得。

日志记录,使用过滤器不能细致地划分模块,此时应该考虑拦截器。然而拦截器也是依据URL做规则匹配,因此相对来说不够细致,因此我们会考虑到使用AOP实现,AOP可以针对代码的方法级别做拦截,很适合日志功能。

AOP相关

  • 方面(Aspect):一个关注点的模块化,这个关注点实现可能另外横切多个对象。事务管理是J2EE应用中一个很好的横切关注点例子。方面用Spring的Advisor或拦截器实现。
  • 连接点(Joinpoint):程序执行过程中明确的点,如方法的调用或特定的异常被抛出。
  • 通知(Advice):在特定的连接点,AOP框架执行的动作。各种类型的通知包括“around”、“before”和“throws”通知。通知类型将在下面讨论。许多AOP框架包括Spring都是以拦截器做通知模型,维护一个“围绕”连接点的拦截器链。
  • 切入点(Pointcut):指定一个通知将被引发的一系列连接点的集合。AOP框架必须允许开发者指定切入点,例如,使用正则表达式。
  • 引入(Introduction):添加方法或字段到被通知的类。Spring允许引入新的接口到任何被通知的对象。例如,你可以使用一个引入使任何对象实现IsModified接口,来简化缓存。
  • 目标对象(Target Object):包含连接点的对象,也被称作被通知或被代理对象。
  • AOP代理(AOP Proxy):AOP框架创建的对象,包含通知。在Spring中,AOP代理可以是JDK动态代理或CGLIB代理。
  • 编织(Weaving):组装方面来创建一个被通知对象。这可以在编译时完成(例如使用AspectJ编译器),也可以在运行时完成。Spring和其他纯Java 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。

报错处理顺序切面、拦截器、过滤器。

你可能感兴趣的:(过滤器,spring,java,aop)