springboot工程中切入方式浅析

springboot工程中有多种切入的方式,如aop、filter、interceptor、listener、resolver。下面以白名单校验的业务场景进行简单的分析。
1、aop方式
aop是springboot提供的面向切面编程,只需在方法前添加切点,然后再对切点进行处理即可。
首先定义一个注解Whitelist,然后使用@Aspect注解声明一个切面类WhitelistAspect,最后在执行被注解的方法时才会进行切面类的处理方法中。

@Aspect
@Component
public class WhitelistAspect {

    @Pointcut("@annotation(com.***.Whitelist)")
    public void whitelistPointcut() {}

    @Pointcut("execution(* com.***..*.*(..))")
    public void pointcut(){}

    @Before(value = "whitelistPointcut() && @annotation(whitelist)")
    public void checkWhitelist(JoinPoint joinPoint, Whitelist whitelist) {
        System.out.println("checkWhitelist");
        // 可使用 joinPoint.getArgs() 获取Controller方法的参数
        // 可使用 whitelist 变量获取注解参数
    }

    @Before(value = "@annotation(whitelist)")
    public void checkWhitelist2(JoinPoint joinPoint, Whitelist whitelist) {
        System.out.println("checkWhitelist2");
        // 可使用 joinPoint.getArgs() 获取Controller方法的参数
        // 可使用 whitelist 变量获取注解参数
    }

    // 须去掉, Whitelist whitelist,否则报错
    @Before(value = "pointcut()")
    public void checkWhitelist3(JoinPoint joinPoint) {
        System.out.println("checkWhitelist3");
        // 可使用 joinPoint.getArgs() 获取Controller方法的参数
    }

}

2、filter方式
filter并不是spring提供的,是在servlet规范中定义的,对指定的url进行切入,被过滤的请求不会派发到spring容器中。

public class WhitelistFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // 初始化后被调用一次
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        // 判断是否需要拦截
        System.out.println("doFilter");
        chain.doFilter(request, response);
    }

    @Override
    public void destroy() {
        // 被销毁时调用一次
    }
}

3、interceptor方式
interceptor拦截器用在在controller内action被执行前进行切入校验释放要执行。

@Component
public class WhitelistInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        Whitelist whitelist = ((HandlerMethod) handler).getMethodAnnotation(Whitelist.class);
        // whitelist.values(); 通过 request 获取请求参数,通过 whitelist 变量获取注解参数
        System.out.println("preHandle");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        // 方法在Controller方法执行结束后执行
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        // 在view视图渲染完成后执行
    }
}

4、listener方式
listener监听器实现ServletRequestListener 接口,对请求进行切入。

public class MyHttpRequestListener implements ServletRequestListener {

    @Override
    public void requestInitialized(ServletRequestEvent servletRequestEvent) {
        HttpServletRequest request = (HttpServletRequest) servletRequestEvent.getServletRequest();
        String requestUri = request.getRequestURI();
        log.info("进入监听器的请求地址是:{}", requestUri);
    }

    @Override
    public void requestDestroyed(ServletRequestEvent servletRequestEvent) {
        log.info("监听器被销毁");
    }
}

5、resolver方式
参数解析器是 Spring 提供的用于解析自定义参数的工具,Spring 会维护一个 ResolverList, 在请求到达时,Spring 发现有自定义类型参数(非基本类型), 会依次尝试这些 Resolver,直到有一个 Resolver 能解析需要的参数。要实现一个参数解析器,需要实现 HandlerMethodArgumentResolver 接口。
首先定义自定义参数类型 AuthParam,然后定义 AuthParamResolver 并实现 HandlerMethodArgumentResolver 接口,最后在 Controller Action 方法上签名内添加 AuthParam 参数以启用此 Resolver;

public class AuthParam implements Serializable {

    private static final long serialVersionUID = 1L;

    private String name = "hello2";

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
@Component
public class AuthParamResolver implements HandlerMethodArgumentResolver {

    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.getParameterType().equals(AuthParam.class);
    }

    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        Whitelist whitelist = parameter.getMethodAnnotation(Whitelist.class);
        // 通过 webRequest 和 whitelist 校验白名单
        System.out.println("resolveArgument");
        return new AuthParam();
    }
}

通过配置的方式将上面注入spring

@Configuration
public class MvcConfiguration implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new WhitelistInterceptor()).addPathPatterns("/*").order(1);
        // 这里可以配置拦截器启用的 path 的顺序,在有多个拦截器存在时,任一拦截器返回 false 都会使后续的请求方法不再执行
    }

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        argumentResolvers.add(new AuthParamResolver());
    }

    @Bean
    public FilterRegistrationBean someFilterRegistration() {
        FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setFilter(new WhitelistFilter());
        registration.addUrlPatterns("/*");
        registration.setName("whitelistFilter");
        registration.setOrder(1);
        return registration;
    }
}

测试示例

	@Whitelist
    @RequestMapping("/hello")
    public Object hello() {
        return "hello";
    }

    @RequestMapping("/hello2")
    public Object hello2(AuthParam authParam) {
        return "hello2";
    }

通过测试可知不同切入方式的执行顺序如下,filter是servlet实现的,自然是最先被调用,后续被调用的是interceptor被拦截了自然不需要后续再进行处理,然后是resolver,最后才是aop。在使用的过程中还需根据具体的业务需求来选择。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值