Spring 拦截器

一、什么是拦截器:

  1. 什么是拦截器:在AOP(Aspect-Oriented Programming)中用于在某个方法或字段被访问之前,进行拦截然后在之前或之后加入某些操作。拦截是AOP的一种实现策略
  2. 为什么需要拦截器:在做身份认证或者是进行日志的记录时,我们需要通过拦截器达到我们的目的。最常用的登录拦截、或是权限校验、或是防重复提交、或是根据业务像12306去校验购票时间,总之可以去做很多的事情
  3. 如何用拦截器:在spring中用拦截器需要实现HandlerInterceptor接口或者它的实现子类:HandlerInterceptorAdapter,同时在applicationContext.xml文件中配置拦截器。
    拦截器可以做什么?
  4. 用拦截器做很多事情:

    日志记录:记录请求信息的日志,以便进行信息监控、信息统计等;
    权限检查:如登录校验,在处理器处理之前先判断是否已经登录;
    性能监控:通过拦截器在进入处理器之前记录开始时间,在处理完后记录结束时间,从而得到该请求的处理时间。
    通用行为:读取 Cookie 得到用户信息并将用户对象放入请求,从而方便后续流程使用。还有如提取 Locale、Theme 信息等,只要是多个处理器都需要的都可以用拦截器来实现。

二、 拦截器实现方法:

spring中拦截器主要分两种,一个是HandlerInterceptor,一个是MethodInterceptor。 

2.1 . HandlerInterceptor拦截器

HandlerInterceptor是springMVC项目中的拦截器,它拦截的目标是请求的地址,比MethodInterceptor先执行。

1.实现拦截器类

@Slf4j
public class LoginCheckHandlerInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        log.info("execute LoginCheckHandlerInterceptor's preHandle method");

        String userId = request.getHeader("userId");
        if (StringUtils.isEmpty(userId)) {
            response.setStatus(401);
            response.getWriter().print("please login");
            response.getWriter().flush();
            return false;
        }

        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        log.info("execute LoginCheckHandlerInterceptor's postHandle method");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        log.info("execute LoginCheckHandlerInterceptor's afterCompletion method");
    }
}

实现一个HandlerInterceptor拦截器可以直接实现HandlerInterceptor接口,也可以继承HandlerInterceptorAdapter类。这两种方法殊途同归,其实HandlerInterceptorAdapter也就是声明了HandlerInterceptor接口中所有方法的默认实现,而我们在继承他之后只需要重写必要的方法。 

下面就是HandlerInterceptorAdapter的代码,可以看到一个方法只是默认返回true,另外两个是空方法:

public abstract class HandlerInterceptorAdapter implements HandlerInterceptor {  
  
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)  
        throws Exception {  
        return true;  
    }  

    public void postHandle(  
            HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)  
            throws Exception {  
    }  
  
    public void afterCompletion(  
            HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)  
            throws Exception {  
    }  
}  
  • preHandle

用来拦截处理器的执行,preHandle方法将在Controller处理之前调用的。SpringMVC里可以同时存在多个interceptor,它们基于链式方式调用,每个interceptor都根据其声明顺序依次执行。这种链式结构可以中断,当方法返回false时整个请求就结束了。

方法的返回值是布尔类型,方法如果返回false,那后面的interceptor和Controller都不会执行(通常都会响应一个自定义的 Http 错误码给客户端)。如果返回值为true,则接着调用下一个interceptor的preHandle方法。如果当前是最后一个interceptor,接下来就会直接调用Controller的处理方法。

  • postHandle:

postHandle 会在Controller方法调用之后,但是在DispatcherServlet 渲染视图之前调用。因此我们可以在这个阶段,对将要返回给客户端的ModelAndView进行操作。

  • afterCompletion:

afterCompletion在当前interceptor的preHandle方法返回true时才执行。该方法会在整个请求处理完成后被调用,就是DispatcherServlet渲染视图完成以后,主要是用来进行资源清理工作。需要注意的是,afterCompletion在interceptor链式结构中以相反的顺序执行,也就是说先申明的interceptor返回会后调用。

拦截器内方法的执行顺序依次是:preHandle ---> postHandle ---> afterCompletion。

2、在配置类定义拦截路径

@Configuration
public class IntercepterConfig implements WebMvcConfigurer {
    @Autowired
    private EasyLogControllerInterceptor easyLogControllerInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //addPathPatterns用于添加拦截路径
        //excludePathPatterns用于添加不拦截的路径
        registry.addInterceptor(LoginCheckHandlerInterceptor ).addPathPatterns("/hello");
    }
    //此方法用于配置静态资源路径
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/**").addResourceLocations("classpath:/my/");
    }
}

3、定义测试controller类

@RestController
public class TestController {

    @GetMapping("/hello")
    public Map<String,String> hello(){
        Map<String,String> response=new HashMap<>();
        response.put("msg","hello");
        return response;
    }

}

配置完后可以运行程序,访问/hello ,控制台将打印对应的请求日志

拦截器调用顺序

在调用hander之前分别调用每个HandlerInterceptor拦截器的preHandle方法,若有一个拦截器返回false,则会调用triggerAfterCompletion方法,并且立即返回不再往下执行;若所有的拦截器全部返回true并且没有出现异常,则调用handler返回ModelAndView对象;再然后分别调用每个拦截器的postHandle方法;最后,即使是之前的步骤抛出了异常,也会执行triggerAfterCompletion方法。

 

 2.2. MethodInterceptor拦截器

MethodInterceptor是AOP项目中的拦截器(注:不是动态代理拦截器),区别与HandlerInterceptor拦截目标时请求,它拦截的目标是方法。

实现MethodInterceptor拦截器大致也分为两种:

(1)MethodInterceptor接口;

(2)利用AspectJ的注解配置;

   spring框架的AOP非常强大,可以实现前置增强、后置增强、最终增强、环绕增强等处理,另外还可以对方法入参进行控制过滤,而影响目标方法的执行中的状态。AOP都是基于(Cglib)代理模式实现的,其中的关键点在于实现MethodInterceptor接口,在其“public Object invoke(MethodInvocation mi)”方法中制定代理方法的生成策略,而从此方法的MethodInvocation类型参数mi中可以获得目标对象、方法的参数、目标方法等信息,根据这些信息可以精确地控制增强效果。

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        

 

public static class UserMethodInterceptor implements MethodInterceptor {
 
    @Override
    public Object invoke(MethodInvocation mi) throws Throwable {
        String methodName = mi.getMethod().getName();// 方法名
        Object returnVal = null;
 
        /*
         * 根据方法名的不同设置不同的拦截策类 似于配置文件中的<aop:pointcut
         * expression="execution( xxxmethodName)"/>,以此定义切入点。
         *
         * spring框架中可以根据方法对应的包、对应的参数、返回值、方法名等多个条件,并结合正则表达式来定义切入点
         * 为了简单化,此处我只用方法名的前缀来定义切入点。
         *
         */
        if (methodName.startsWith("add")) {
 
            returnVal = beforeEnhance(mi);
 
        } else if (methodName.startsWith("delete")) {
            returnVal = afterThrowingEnhance(mi);
        } else {
            returnVal = mi.proceed();
        }
 
        return returnVal;
    }
 
    /*
     * 前置增强策略
     */
    private Object beforeEnhance(MethodInvocation mi) throws Throwable {
        /*
         * spring框架通过配置文件或注解来获得用户自定义的增强方法名及对其JavaBean的类名, 然后通过反射机制动态地调用用户的增强方法。
         * 为了简单化的测试,我这里将增强方法及对应的类给定死了、不能改动, 增强JavaBean就是ConsoloEnhancer,
         * 增强方法就是"public void before(MethodInvocation mi)"
         *
         */
        new ConsoloEnhancer().before(mi); // 调用用户定义的前置处理方式
        Object returnVal = mi.proceed(); // 执行目标方法
        return returnVal;
    }
 
    /*
     * 异常处理策略
     */
    private Object afterThrowingEnhance(MethodInvocation mi) throws Throwable {
        try {
            return mi.proceed();// 执行目标方法
        } catch (Throwable e) {
            new ConsoloEnhancer().afterThrowing(mi, e); // 调用用户定义的异常处理方式
            throw e;
 
        }
 
    }
}

自定义一个拦截器(实际上是一个拦截器)

         这个Advice通知实现了 MethodInterceptor接口,而MethodInterceptor接口继承了Interceptor接口,Intercepto接口又继承了Advice接口,因此我个将这拦截器称为一个通知。

spring框架中,当读到配置文件的"<aop:before>"标签,就安排一个AspectJMethodBeforeAdvice类(继承于AbstractAspectAdive抽象类,此抽象类实现了Advice接口)去处理前置增强;当读到配置文件的"<aop:after-throwing>"标签,则会安排一AspectJAfterThrowingAdvice类(实现了MethodInterceptor接口)去做异常增强处理。spring框架切实做到了遵循”单一职责“原则,专门组织一个类去处理一种类型(前置、后置或最终等)的增强。

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring框架中,拦截器是一种可以拦截请求并在处理请求之前或之后执行自定义逻辑的组件。它们可以用于实现身份验证、授权、日志记录等功能。下面是使用拦截器的步骤: 1. 创建一个类并实现`HandlerInterceptor`接口。 2. 在类中实现`preHandle`、`postHandle`和`afterCompletion`方法,分别在请求处理之前、请求处理之后和视图渲染完成后执行特定的逻辑。 3. 在配置文件中注册拦截器,并指定拦截的URL模式或特定路径。 以下是一个简单的例子: ```java public class CustomInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 在请求处理之前执行的逻辑 return true; // 返回true继续处理请求,返回false终止请求处理 } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { // 在请求处理之后但视图渲染之前执行的逻辑 } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { // 在视图渲染完成后执行的逻辑 } } ``` 在配置文件中注册拦截器: ```xml <mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/**"/> <!-- 拦截所有URL --> <bean class="com.example.CustomInterceptor"/> </mvc:interceptor> </mvc:interceptors> ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值