Фильтры и перехватчики весной

Spring

фильтр

Что такое фильтр?

Подобно сервлетам, фильтры — это компоненты веб-приложения, которые можно привязать к веб-приложению. Но в отличие от других компонентов веб-приложений, фильтры «привязаны» к обработке контейнера. Это означает, что они могут получить доступ к запросу до того, как он достигнет сервлета, а также могут перехватить ответную информацию до того, как она будет возвращена клиенту. Этот доступ позволяет фильтрам проверять и изменять содержимое запросов и ответов.

Каковы методы интерфейса Filter?

в этом :

Инициализация фильтра вызывается, когда контейнер сервлета создает экземпляр фильтра, чтобы убедиться, что фильтр может работать правильно. Когда во время выполнения метода init() возникают следующие проблемы, веб-контейнер не настроит его так, чтобы он был доступен:

  • Бросить исключение сервлета
  • не вернулся в течение времени, установленного веб-контейнером

делатьФильтр :

Основной метод Filter используется для выполнения некоторых операций с множествами над каждым перехваченным запросом.

Типичные варианты использования следующие:

  • Перехватите HttpServletRequest клиента до того, как HttpServletRequest достигнет сервлета.
  • Осмотрите HTTPServletRequest по мере необходимости, а также измените заголовки и данные HTTPServletRequest.
  • Перехватите HttpServletResponse до того, как он достигнет клиента.
  • При необходимости проверьте HttpServletResponse, вы можете изменить заголовки и данные HttpServletResponse.

уничтожение:

Уничтожение фильтра вызывается, когда контейнер сервлета уничтожает экземпляр фильтра, чтобы освободить занятые им ресурсы. Этот метод будет вызываться веб-контейнером только после завершения или истечения времени ожидания всех потоков в методе doFilter().

Как реализовать собственный фильтр

Сначала нам нужно создать класс, реализующий интерфейс Filter, а затем переопределить методы в интерфейсе:

package com.demo.demofilter.demofilter.filter;

import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

@Component
@Order(1)   // 过滤顺序,值越小越先执行
@WebFilter(urlPatterns = "/demoFilter", filterName = "filterTest")
public class DemoFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("filter初始化中...");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

        System.out.println("\ndoFilter()开始执行:发往 " + ((HttpServletRequest) servletRequest).getRequestURL().toString() + " 的请求已被拦截");

        System.out.println("检验接口是否被调用,尝试获取contentType如下: " + servletResponse.getContentType());
        // filter的链式调用;将请求转给下一条过滤链
        filterChain.doFilter(servletRequest, servletResponse);
        System.out.println("检验接口是否被调用,尝试获取contentType如下: " + servletResponse.getContentType());

        System.out.println("doFilter()执行结束。\n");
    }

    @Override
    public void destroy() {
        System.out.println("filter销毁中...");
    }
}

Затем создайте контроллер и укажите два пути запроса:

package com.demo.demofilter.demofilter.filter;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("demoFilter")
public class FilterController {

    @GetMapping("hello")
    public String hello() {
        System.out.println("接口被调用:hello() ");
        return "hello filter";
    }

    @GetMapping("hi")
    public String hi() {
        System.out.println("接口被调用:hi()");
        return "hi filter";
    }
}

Запускаем проект, видим, что наш фильтр успешно инициализировался с запуском программы

Отправьте запросы на эти два интерфейса соответственно:

Наконец, остановите проект, тогда фильтр будет уничтожен.

Что такое связанный вызов фильтра?

Когда мы настраиваем несколько фильтров и запрос может быть перехвачен несколько раз, запрос будет следовать客户端 -> 过滤器1 -> 过滤器2 -> servlet -> 过滤器2 -> 过滤器1 -> 客户端Цепной поток, как показано ниже:

В приведенном выше примере кода, поскольку мы определяем фильтр только при выполненииfilterChain.doFilter(servletRequest, servletResponse);, запрос будет напрямую переадресован сервлету для вызова.

Так что нам нужно немного переделать его и посмотреть, что произойдет, когда мы добавим еще один DemoFilter2:

package com.demo.demofilter.demofilter.filter;

import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

@Component
@Order(2)   // 过滤顺序,值越小越先执行,值相同或不指定时按filterName排序
// 注意这里的urlPatterns要与前面保持一致
@WebFilter(urlPatterns = "/demoFilter", filterName = "filterTest2") 
public class DemoFilter2 implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("filter2初始化中...");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

        System.out.println("\ndoFilter2()开始执行:发往 " + ((HttpServletRequest) servletRequest).getRequestURL().toString() + " 的请求已被拦截");

        System.out.println("检验接口是否被调用,尝试获取contentType如下: " + servletResponse.getContentType());
        filterChain.doFilter(servletRequest, servletResponse);
        System.out.println("检验接口是否被调用,尝试获取contentType如下: " + servletResponse.getContentType());

        System.out.println("doFilter2()执行结束。\n");
    }

    @Override
    public void destroy() {
        System.out.println("filter2销毁中...");
    }
}

Результаты приведены ниже:

Видно, что когда запрос одновременно удовлетворяет условиям фильтрации нескольких фильтров,filterChain.doFilter()Он будет передан следующему фильтру в определенном порядке (который можно указать с помощью @Order), пока не попадет в сервлет для фактического вызова интерфейса. После завершения вызова ответ вернется по тому же пути и после повторного прохождения каждого фильтра наконец прибудет к клиенту.

Перехватчик Перехватчик

Что такое перехватчик?

Перехватчик — это стратегия реализации АОП, которая используется для перехвата метода или поля перед доступом к ним, а затем добавления некоторой операции до или после него. Как и фильтр, перехватчик также связан. Каждый вызов перехватчика выполняется в том порядке, в котором он объявлен. В целом перехватчики можно использовать следующими способами:

  • Ведение журнала: Журналы информации о запросах вероятности для мониторинга информации, статистики информации и т. Д.
  • Проверка разрешений: проверьте права доступа пользователя, аутентификацию или авторизацию и т. д.
  • Мониторинг производительности: запишите время начала и время окончания до и после входа в процессор через перехватчик, чтобы получить время обработки запроса.
  • Обычное поведение: прочитайте файл cookie, чтобы получить информацию о пользователе, и поместите объект пользователя в заголовок запроса, чтобы облегчить использование последующих процессов.

Каковы методы интерфейса перехватчика?

предварительный обработчик:

Метод предварительной обработки метода будет вызываться перед обработкой запроса. Обычно он используется для выполнения некоторых операций предварительной инициализации метода или для выполнения некоторой предварительной обработки текущего запроса; кроме того, его также можно использовать для суждений, таких как проверка разрешений, чтобы определить, следует ли продолжить выполнение запроса.

Этот метод возвращает логическое значение. Если значение равно false, запрос завершается, и последующие перехватчики и контроллеры не будут выполняться; если значение равно true, метод preHandler() следующего перехватчика будет продолжать вызываться. достигнут последний перехватчик, будет вызван Контроллер текущего запроса.

постобработчик:

Постобработка метода, который будет вызываться после обработки запроса. Хотя он выполняется после вызова метода Controller, его вызов все еще происходит до того, как DispatcherServlet визуализирует представление и возвращает его, поэтому обычно его можно использовать для работы с объектом ModelAndView, обрабатываемым контроллером.

после завершения:

Выполняется после завершения всей обработки запроса (включая отрисовку представления), в основном используется для очистки некоторых ресурсов.

Как реализовать собственный перехватчик

Аналогичным образом сначала создайте класс, реализующий интерфейс HandlerInterceptor, а затем переопределите методы интерфейса:

package com.demo.demofilter.demofilter.interceptor;

import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Component
public class DemoInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
        System.out.println("afterHandle");
    }
}

Далее вам необходимо зарегистрировать перехватчик, указав, какой перехватчик использовать, и URL-адрес, соответствующий перехватчику:

package com.demo.demofilter.demofilter.interceptor;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class InterceptorConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 如果有多个拦截器,继续registry.add往下添加就可以啦
        registry.addInterceptor(new DemoInterceptor()).addPathPatterns("/demoInterceptor/**");
    }

}

Наконец, контроллер

package com.demo.demofilter.demofilter.interceptor;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("demoInterceptor")
public class InterceptorController {

    @GetMapping("hello")
    public String hello() {
        System.out.println("接口被调用:hello() ");
        return "hello interceptor";
    }

    @GetMapping("hi")
    public String hi() {
        System.out.println("接口被调用:hi()");
        return "hi interceptor";
    }
}

Результаты приведены ниже:

Другие соображения

В процессе выполнения Http-запроса необходимы следующие шаги:

  1. Запросы перехватываются DispatcherServlet
  2. DispatcherServlet сопоставляет полученный URL-адрес с соответствующим контроллером.
  3. Запрос обрабатывается перехватчиком до того, как запрос достигнет соответствующего контроллера.
  4. После завершения обработки представление анализируется
  5. вернуться к просмотру

Таким образом, только запросы, проходящие через DispatcherServlet, будут перехвачены перехватчиком, а наши пользовательские запросы Servlet не будут перехвачены.

Фильтры и перехватчики

Как типичная реализация идей АОП, фильтры имеют много общего и перехватчиков. И самая большая разница между ними заключается в следующем:Фильтры определены в спецификации сервлета, контейнер поддерживается сервлетом; пружинные перехватыватели в контейнере, поддерживаемом пружинной структурой.

Поэтому, как компонент Spring, перехватчиком можно управлять через IOC-контейнер, получать в нем каждый экземпляр bean-компонента и вызывать в spring различные ресурсы и объекты, такие как объекты Service, источники данных, управление транзакциями и т. д.; в то время как фильтровать нет.

В целом, два основных различия существуют в следующих аспектах:

  • Фильтры — это обратные вызовы на основе функций, а перехватчики основаны на механизмах отражения Java.
  • Фильтры могут изменить запрос, в то время как перехватчики не могут
  • Фильтр должен быть реализован в контейнере сервлета, а перехватчик может применяться к различным средам, таким как JavaEE, JavaSE и т. д.
  • Перехватчики могут вызывать различные зависимости в IOC-контейнере, в то время как фильтры не могут
  • Фильтры можно использовать только до и после запроса, а перехватчики можно детализировать для каждого метода.

Наконец, добавьте две картинки, хе-хе:

  1. Фильтр, сервлет, время срабатывания перехватчика

    Время срабатывания фильтра — после контейнера и до сервлета, поэтому триггер фильтраdoFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)Входным параметром является ServletRequest, а не HttpServletRequest, поскольку фильтр находится перед HttpServlet.

  2. Отфильтровать шаги запуска перехватчика

    На втором этапе механизм SpringMVC заключается в распределении запросов по разным контроллерам с помощью DispaterServlet, Фактически этот шаг выполняется в методе service() сервлета.