过滤器(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。
报错处理顺序切面、拦截器、过滤器。