当前位置: 代码迷 >> 综合 >> Filter,Interceptor,AOP
  详细解决方案

Filter,Interceptor,AOP

热度:8   发布时间:2023-12-15 13:01:13.0

一个请求先后经历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";@Overridepublic void init(FilterConfig filterConfig) throws ServletException {
    }@Overridepublic 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过滤器 结束");}@Overridepublic 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*/@Overridepublic 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*/@Overridepublic 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*/@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    System.out.println("拦截器 afterCompletion 执行");}
}// WebConfig拦截器注册中心:
@Configuration
public class WebInterceptorConfig extends WebMvcConfigurationSupport {
    @Autowiredprivate AuthInterceptor authInterceptor;@Overrideprotected 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的

  相关解决方案