0
点赞
收藏
分享

微信扫一扫

SpringBoot 之AOP实现过滤器、拦截器、切面

小贴贴纸happy 2022-03-25 阅读 57

文章目录

AOP概述

AOP表示面向切面编程(Aspect-Oriented Programming)。
首先AOP不是一项技术,它是一种编程思想。
过滤器、拦截器、切面都可以理解为AOP的实现方式。

过滤器、拦截器、切面区别

过滤器拦截器Aspect
关注的点所有web请求部分web请求偏向于业务层面的拦截
实现原理函数回调JAVA反射机制(动态代理)动态代理
Servlet提供的支持Filter接口
Spring提供的支持HandlerInterceptorAdapter类、HandlerInterceptor接口@Aspect
可能需要实现的方法void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)Object around(ProceedingJoinPoint joinPoint)

过滤器 Filter

简单来讲就是用来过滤东西的,比如:统一设置编码,统一设置所有请求头信息。

实现: 实现 javax.Servlet.Filter 接口就可以创建一个过滤器。

使用过滤器统一请求耗时

/**
 * 统一记录请求耗时
 */
@Slf4j
@WebFilter(filterName = "ApiAccessFilter", urlPatterns = "/*")
public class ApiAccessFilter implements Filter {

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
                         FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        long start = System.currentTimeMillis();
        filterChain.doFilter(servletRequest, servletResponse);
        long duration = System.currentTimeMillis() - start;
        if (duration > 200) {
            log.info("Api Access uri: {}, method: {}, client: {}, duration: {}ms , time is too long",
                    request.getRequestURI(), request.getMethod(), getIP(request), System.currentTimeMillis() - start);
        }else{
            log.info("Api Access uri: {}, method: {}, client: {}, duration: {}ms",
                    request.getRequestURI(), request.getMethod(), getIP(request), System.currentTimeMillis() - start);
        }

    }

    private String getIP(HttpServletRequest request) {
        if (request == null) {
            return "0.0.0.0";
        }
        String Xip = request.getHeader("X-Real-IP");
        String XFor = request.getHeader("X-Forwarded-For");
        String UNKNOWN_IP = "unknown";
        if (StringUtils.isNotEmpty(XFor) && !UNKNOWN_IP.equalsIgnoreCase(XFor)) {
            //多次反向代理后会有多个ip值,第一个ip才是真实ip
            int index = XFor.indexOf(",");
            if (index != -1) {
                return XFor.substring(0, index);
            } else {
                return XFor;
            }
        }
        XFor = Xip;
        if (StringUtils.isNotEmpty(XFor) && !UNKNOWN_IP.equalsIgnoreCase(XFor)) {
            return XFor;
        }
        if (StringUtils.isBlank(XFor) || UNKNOWN_IP.equalsIgnoreCase(XFor)) {
            XFor = request.getHeader("Proxy-Client-IP");
        }
        if (StringUtils.isBlank(XFor) || UNKNOWN_IP.equalsIgnoreCase(XFor)) {
            XFor = request.getHeader("WL-Proxy-Client-IP");
        }
        if (StringUtils.isBlank(XFor) || UNKNOWN_IP.equalsIgnoreCase(XFor)) {
            XFor = request.getHeader("HTTP_CLIENT_IP");
        }
        if (StringUtils.isBlank(XFor) || UNKNOWN_IP.equalsIgnoreCase(XFor)) {
            XFor = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (StringUtils.isBlank(XFor) || UNKNOWN_IP.equalsIgnoreCase(XFor)) {
            XFor = request.getRemoteAddr();
        }
        return XFor;
    }
}

拦截器 Interceptor

在执行方法之前,进行拦截,然后在之前或之后加入一些操作。

使用场景:

  • 日志记录: 记录请求信息的日志,以便进行信息监控、信息统计、计算PV(Page View)等。
  • 权限检查: 如登录检测,进入处理器检测检测是否登录。
  • 性能监控: 通过拦截器在进入处理器之前记录开始时间,在处理完后记录结束时间,从而得到该请求的处理时间。
  • 通用行为: 读取cookie得到用户信息并将用户对象放入请求,从而方便后续流程使用,只要是多个处理器都需要的即可使用拦截器实现。

实现拦截器: 实现 HandlerInterceptor 接口就可以创建一个拦截器。

HandlerInterceptor 接口的源码如下:

public interface HandlerInterceptor {
  	// 在请求处理之前进行调用(Controller方法调用之前)
	default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		return true;
	}
  	// 请求处理之后进行调用,但是在视图被渲染之前(Controller方法调用之后),如果异常发生,则该方法不会被调用
	default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
			@Nullable ModelAndView modelAndView) throws Exception {
	}
  	// 在整个请求结束之后被调用,也就是在 DispatcherServlet 渲染了对应的视图之后执行(主要是用于进行资源清理工作
	default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
			@Nullable Exception ex) throws Exception {
	}
}

使用拦截器判断是否登录

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface NotCheckToken {
}

@Component
public class AuthenticationIntercept extends HandlerInterceptorAdapter {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        if (handler instanceof HandlerMethod) {
            HandlerMethod method = (HandlerMethod) handler;
            NotCheckToken methodAnnotation = method.getMethodAnnotation(NotCheckToken.class);
            if (Objects.nonNull(methodAnnotation)) {
                return true;
            }
        }
        String userId = request.getHeader("userId");
        if (StringUtils.isBlank(userId)) {
            throw new Exception("未登录");
        }
        request.setAttribute("userId", userId);
        return super.preHandle(request, response, handler);
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
       
    }
}

拦截器注册:

@Configuration
public class WebAppConfigurer implements WebMvcConfigurer {
    @Autowired
    private AuthenticationIntercept authenticationIntercept;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        InterceptorRegistration registration = registry.addInterceptor(authenticationIntercept);
        // 拦截所有请求
        registration.addPathPatterns("/**");
        // 添加不拦截路径
        registration.excludePathPatterns("/login", "/error", "/logout","/login.html");
    }
}

切面 AspectJ

https://blog.csdn.net/const_/article/details/93502628

举报

相关推荐

0 条评论