Filter,Interceptor,AOP

一个请求先后经历Filter,Interceptor,AOP的过程:
在这里插入图片描述

  • 一个请求过来 ,先进行过滤器处理,看程序是否受理该请求 。
  • 过滤器放过后 , 程序中的拦截器进行处理
  • 处理完后进入被 AOP动态代理重新编译过的主要业务类进行处理
    在这里插入图片描述
    ===========before doFilter1
    ===========before doFilter2
    ===========HandlerInterceptorAll preHandle
    ===========HandlerInterceptor1 preHandle
    ===========HandlerInterceptor2 preHandle
    执行Controller
    Controller return前
    ===========HandlerInterceptor2 postHandle
    ===========HandlerInterceptor1 postHandle
    ===========HandlerInterceptorAll preHandle
    Controller return后,Jsp加载完成
    ===========HandlerInterceptor2 afterCompletion
    ===========HandlerInterceptor1 afterCompletion
    ===========HandlerInterceptorAll preHandle
    ===========before doFilter2
    ===========before doFilter1

Filter

Filter:依赖于servlet容器。在实现上,基于函数回调,它可以对几乎所有请求进行过滤,一个过滤器实例只能在容器初始化时调用一次,和框架无关,过滤器可以拦截到方法的请求和响应(ServletRequest request, ServletResponse response),并对请求响应做出过滤操作,比如设置字符编码,鉴权操作等

Filter 是在 Servlet 规范中定义的,是 Servlet 容器支持的,Filter只在 Servlet 前后起作用

  • 在HttpServletRequest 到达Servlet 之前,拦截客户的HttpServletRequest
  • 根据需要检查HttpServletRequest ,也可以修改HttpServletRequest 头和数据
  • 根据需要检查HttpServletResponse ,可以修改HttpServletResponse 头和数据

代码实现示例:对请求接口的过滤

@Component
public class AuthFilter implements Filter {
    private static final String URL_PREFIX = "/sample/api/v1";

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
        String requestURI = httpServletRequest.getRequestURI();
        if (!requestURI.contains(URL_PREFIX)) {
            log.warn("请求地址无效");
            return;
        }
        System.out.println("Filter过滤器 开始");
        filterChain.doFilter(servletRequest, servletResponse);
        System.out.println("Filter过滤器 结束");
    }

    @Override
    public void destroy() {
        log.debug("destroy");
    }

Interceptor

依赖于web框架,在SpringMVC中就是依赖于SpringMVC框架。在实现上,基于Java的反射机制,属于面向切面编程(AOP)的一种运用,可以控制请求的控制器和方法,但控制不了请求方法里的参数(用于处理页面提交的请求响应并进行处理,例如做国际化,做主题更换,过滤等)。

拦截器是在 Spring容器内的,是Spring框架支持的,拦截器能够深入到方法前后、异常抛出前后等。

  • 通过实现HandlerInterceptor接口或者继承HandlerInterceptorAdapter抽象类,
  • 复写preHandle()、postHandle()和afterCompletion()来对用户的请求进行拦截处理

代码实现示例:

@Component
@Slf4j
public class AuthInterceptor implements HandlerInterceptor {
    /**
     * 处理器实际执行 之前 会被执行
     *
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Method method = handlerMethod.getMethod();
        Auth annotation = method.getAnnotation(Auth.class);
        if (Objects.nonNull(annotation) && !annotation.value()) {
            throw new RuntimeException("用户没有操作权限");
        }
        System.out.println("拦截器 preHandle 执行");
        return true;
    }

    /**
     * 处理器执行 完毕 以后被执行
     *
     * @param request
     * @param response
     * @param handler
     * @param modelAndView
     * @throws Exception
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("拦截器 postHandle 执行");
    }

    /**
     * 整个请求处理完成 之后被执行
     *
     * @param request
     * @param response
     * @param handler
     * @param ex
     * @throws Exception
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("拦截器 afterCompletion 执行");
    }
}

// WebConfig拦截器注册中心:
@Configuration
public class WebInterceptorConfig extends WebMvcConfigurationSupport {
    @Autowired
    private AuthInterceptor authInterceptor;

    @Override
    protected void addInterceptors(InterceptorRegistry registry) {
        // 多个拦截器组成一个拦截器链
        // addPathPatterns 用于添加拦截规则,/**表示拦截所有请求
        // excludePathPatterns 用户排除拦截
        registry.addInterceptor(authInterceptor).addPathPatterns("/**").excludePathPatterns();
        super.addInterceptors(registry);
    }
}

AOP

是一种编程思想,对OOP的补充,常见实现:Filter和拦截器,AOP使用的主要是动态代理,AOP操作可以对操作进行横向的拦截,最大的优势在于他可以获取执行方法的参数,对方法进行统一的处理.常见使用日志,事务,请求参数安全验证等

AOP的一些概念:

  • 通知(增强)Advice:定义具体要插入的内容,以及在连接点插入的时机,通过beforeafter等语义关键词表示。

    • Advice通知的类型。Spring 中可以分为以下几种:
      • before: 前置通知,在连接点前调用。
      • after: 后置通知,在连接点后调用。
      • afterReturning: 返回通知,在连接点方法执行并正常返回后调用
      • afterThrowing: 异常通知,当连接点方法异常时调用
      • Around: 环绕通知
  • 连接点 Join point:连接点,表示在程序中明确定义的点,典型的包括方法调用,对类成员的访问以及异常处理程序块的执行等等。也就是我们要插入代码的地方。

  • 切点 Pointcut: 连接点的集合。用来描述连接点。如通过通配符、正则表达式等语法来定义连接点。实际执行时代码是插入到某个具体的连接点。

    • 切点匹配规则

      • 任意公共方法的执行:
        execution(public * *(..))
        public可以省略, 第一个* 代表方法的任意返回值 第二个参数代表任意包+类+方法 (…)任意参数

      • 任何一个以“set”开始的方法的执行:
        execution(* set*(..))

      • UserService接口的任意方法:
        execution(* com.coffee.service.UserService.*(..))

      • 定义在com.coffee.service包里的任意方法的执行:
        execution(* com.coffee.service.*.*(..))
        第一个 .* 代表任意类, 第二个 .* 代表任意方法

      • 定义在service包和所有子包里的任意类的任意方法的执行:
        execution(* com.coffee.service..*.*(..))

        …* 代表任意包或者子包

      • 定义在com.coffee包和所有子包里的UserService类的任意方法的执行:

        execution(* com.coffee..UserService.*(..))")

  • 切面 Aspect: 连接点,切点,通知的载体。由这三者组合起来,才能称为一个完整的切面。面向切面编程,也即是将我们想要新增的不影响主逻辑的代码抽出,并将其插入到原有目标代码的过程。

  • 引入 Introduction: 向现有的类添加新方法属性

  • 织入 Weaving : 切面应用到目标对象来创建新的代理对象的过程
    代码实现示例:

@Aspect
@Component
public class AopExec {
    /**
     * 定义切入点,切入点为com.barry.sample.controller.example中的所有函数
     * 通过@Pointcut注解声明频繁使用的切点表达式
     */
    @Pointcut("execution(* com.barry.sample.controller..*.*(..)))")
    public void brokeAspect() {

    }

    /**
     * 前置通知 在连接点执行之前执行的通知
     */
    @Before(value = "brokeAspect()")
    public void beforeExec() {
        System.out.println("AOP 前置通知 before");
    }

    /**
     * @description 在连接点执行之后执行的通知
     */
    @After(value = "brokeAspect()")
    public void afterExec() {
        System.out.println("AOP 后置通知 after");
    }

    /**
     * @description 在连接点方法执行并正常返回后调用
     */
    @AfterReturning(value = "brokeAspect()")
    public void afterReturningExec() {
        System.out.println("AOP 正常返回通知 AfterReturning");
    }

    /**
     * @description 当连接点方法异常时调用
     */
    @AfterThrowing(value = "brokeAspect()")
    public void afterThrowingExec() {
        System.out.println("AOP 异常返回通知 AfterThrowing");
    }

    @Around(value = "brokeAspect()")
    public void aroundExec(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("环绕前置通知");
        joinPoint.proceed();
        System.out.println("环绕后置通知");
    }
}

过滤器 拦截器 AOP 实测执行顺序

Filter过滤器 开始
拦截器 preHandle 执行
环绕前置通知
AOP 前置通知 before
目标方法执行
环绕后置通知
AOP 后置通知 after
AOP 正常返回通知 AfterReturning
拦截器 postHandle 执行
拦截器 afterCompletion 执行
Filter过滤器 结束

说明:文中的图是从网上pull的

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值