Вопрос. Используйте фильтры, перехватчики и срезы, чтобы получить статистику о времени, затрачиваемом на каждый запрос, и сравните различия и связи между тремя запросами.
фильтр
концепция фильтра
Фильтр взят из J2E и может выглядеть какServlet
"улучшенная версия"технологическая цепочка. Фильтры также могут генерировать ответы на пользовательские запросы, как и сервлеты, но они редко используются для генерирования ответов на пользовательские запросы. Полный процесс использования фильтра: фильтр предварительно обрабатывает пользовательские запросы, затем передает запрос сервлету для предварительной обработки и генерирует ответ, и, наконец, фильтр выполняет постобработку ответа сервера.
действие фильтра
Роль нескольких фильтров указана в JavaDoc.
* Examples that have been identified for this design are<br>
* 1) Authentication Filters, 即用户访问权限过滤
* 2) Logging and Auditing Filters, 日志过滤,可以记录特殊用户的特殊请求的记录等
* 3) Image conversion Filters
* 4) Data compression Filters <br>
* 5) Encryption Filters <br>
* 6) Tokenizing Filters <br>
* 7) Filters that trigger resource access events <br>
* 8) XSL/T filters <br>
* 9) Mime-type chain Filter <br>
Для первого элемента, то есть для использования Фильтра для фильтрации разрешений, это можно реализовать следующим образом: определить Фильтр, получить URL-адрес запроса, инициированный каждым клиентом, и сравнить его со списком URL-адресов, на которые текущий пользователь не имеет разрешения. доступ (который можно взять из БД)., который играет роль фильтрации разрешений.
реализация фильтра
Пользовательские фильтры должны быть реализованыjavax.Servlet.Filter
интерфейс и переопределить три метода, определенные в интерфейсе:
-
void init(FilterConfig config)
Используется для завершения инициализации фильтра. -
void destory()
Он используется для завершения переработки некоторых ресурсов до уничтожения Фильтра. -
void doFilter(ServletRequest request,ServletResponse response,FilterChain chain)
Реализуйте фильтрацию, то есть дополнительную предварительную и последующую обработку, добавляемую к каждому запросу и ответу. , Перед выполнением метода выполняется предварительная обработка запроса пользователя, после выполнения метода — постобработка ответа сервера. В частности,chain.doFilter()
Этап предобработки выполняется перед выполнением метода, а окончание выполнения метода означает, что запрос пользователя обработан контроллером. Следовательно, еслиdoFilter
забыл позвонитьchain.doFilter()
метод, запрос пользователя не будет обработан.
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
// 必须添加注解,springmvc通过web.xml配置
@Component
public class TimeFilter implements Filter {
private static final Logger LOG = LoggerFactory.getLogger(TimeFilter.class);
@Override
public void init(FilterConfig filterConfig) throws ServletException {
LOG.info("初始化过滤器:{}", filterConfig.getFilterName());
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
LOG.info("start to doFilter");
long startTime = System.currentTimeMillis();
chain.doFilter(request, response);
long endTime = System.currentTimeMillis();
LOG.info("the request of {} consumes {}ms.", getUrlFrom(request), (endTime - startTime));
LOG.info("end to doFilter");
}
@Override
public void destroy() {
LOG.info("销毁过滤器");
}
private String getUrlFrom(ServletRequest servletRequest){
if (servletRequest instanceof HttpServletRequest){
return ((HttpServletRequest) servletRequest).getRequestURL().toString();
}
return "";
}
}
Как видно из кода, классFilter
вjavax.servlet.*
, поэтому видно, что большое ограничение фильтра заключается в том, чтоОн не может знать, какой контроллер (Контроллер) обрабатывает запрос текущего пользователя., потому что последний определен в структуре Spring.
Зарегистрируйте сторонние фильтры в SpringBoot
Для SpringMvc это можно сделать с помощьюweb.xml
Зарегистрируйте фильтр в . но не существует в SpringBootweb.xml
, в это время, если есть ссылка на фильтр в пакете jar, и этот фильтр не используется в реализации@Component
определен как Spring Bean, этот фильтр не будет действовать. На этом этапе вам необходимо зарегистрировать этот фильтр с помощью java-кода. как определено вышеTimeFilter
Например, при удалении аннотации класса@Component
, метод регистрации:
@Configuration
public class WebConfig {
/**
* 注册第三方过滤器
* 功能与spring mvc中通过配置web.xml相同
* @return
*/
@Bean
public FilterRegistrationBean thirdFilter(){
ThirdPartFilter thirdPartFilter = new ThirdPartFilter();
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean() ;
filterRegistrationBean.setFilter(thirdPartFilter);
List<String > urls = new ArrayList<>();
// 匹配所有请求路径
urls.add("/*");
filterRegistrationBean.setUrlPatterns(urls);
return filterRegistrationBean;
}
}
по сравнению с использованием@Component
Обратите внимание, что этот метод настройки имеет то преимущество, что перехваченный URL-адрес можно свободно настроить.
Перехватчик Перехватчик
Концепция перехватчика
Перехватчики используются в АОП (аспектно-ориентированном программировании) для перехвата метода или поля перед доступом к ним, а затем добавления определенных операций до или после них. Перехват — это стратегия реализации АОП.
Роль перехватчика
- Ведение журнала: запись журнала запроса информации для мониторинга информации, статистики информации, расчета PV (просмотра страницы) и т. д.
- Проверка разрешений: например, обнаружение входа в систему, введите процессор, чтобы определить, следует ли войти в систему
- Мониторинг производительности: перехватчик записывает время начала перед входом в процессор и записывает время окончания после обработки, чтобы получить время обработки запроса. (Обратные прокси, такие как apache, также могут автоматически вести журнал);
- Общее поведение: прочитайте файл cookie, чтобы получить информацию о пользователе, и поместите объект пользователя в запрос, чтобы упростить использование последующих процессов, а также извлечение информации о локали, теме и т. д., если это требуется для нескольких процессоров. , это можно реализовать с помощью перехватчиков.
Реализация перехватчика
путем реализацииHandlerInterceptor
интерфейс и переопределить три метода интерфейса для реализации настройки перехватчика:
-
preHandler(HttpServletRequest request, HttpServletResponse response, Object handler)
метод будет вдо обработки запросапозвонить. в SpringMVCInterceptor
То же, что фильтрцепной вызов. Каждый вызов Interceptor будет выполняться последовательно в соответствии с порядком его объявления, и метод preHandle в Interceptor выполняется первым, поэтому вы можете выполнять некоторые операции предварительной инициализации в этом методе или предварительную обработку текущего запроса, а также некоторые решения могут в этом методе, чтобы решить, должен ли запрос продолжаться. Возвращаемое значение этого метода имеет тип Boolean, когда он возвращает false, это означает, что запрос завершен, и последующие Перехватчики и Контроллеры не будут выполняться, когда возвращаемое значение истинно, метод preHandle следующего Перехватчика будет продолжен. для вызова. , если это последний перехватчик, он вызовет метод контроллера текущего запроса. -
postHandler(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
в текущемПосле обработки запроса, то есть он выполняется после вызова метода Controller, но он будет вызван до того, как DispatcherServlet вернет и отобразит представление, поэтому мы можем работать с объектом ModelAndView после обработки Controller в этом методе. -
afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handle, Exception ex)
Этот метод также выполняется только тогда, когда возвращаемое значение текущего соответствующего метода preHandle Interceptor равно true. Как следует из названия, этот метод будет выполнен после завершения всего запроса, то есть в DispatcherServlet.Выполняется после рендеринга соответствующего представления. Основная функция этого метода заключается в выполнении работы по очистке ресурсов.
@Component
public class TimeInterceptor implements HandlerInterceptor {
private static final Logger LOG = LoggerFactory.getLogger(TimeInterceptor.class);
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
LOG.info("在请求处理之前进行调用(Controller方法调用之前)");
request.setAttribute("startTime", System.currentTimeMillis());
HandlerMethod handlerMethod = (HandlerMethod) handler;
LOG.info("controller object is {}", handlerMethod.getBean().getClass().getName());
LOG.info("controller method is {}", handlerMethod.getMethod());
// 需要返回true,否则请求不会被控制器处理
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
LOG.info("请求处理之后进行调用,但是在视图被渲染之前(Controller方法调用之后),如果异常发生,则该方法不会被调用");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
LOG.info("在整个请求结束之后被调用,也就是在DispatcherServlet 渲染了对应的视图之后执行(主要是用于进行资源清理工作)");
long startTime = (long) request.getAttribute("startTime");
LOG.info("time consume is {}", System.currentTimeMillis() - startTime);
}
В отличие от фильтров, перехватчики используют@Component
После модификации в SpringBoot также нужно пройти реализациюWebMvcConfigurer
Чтобы зарегистрироваться вручную:
// java配置类
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private TimeInterceptor timeInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry){
registry.addInterceptor(timeInterceptor);
}
}
Если он в SpringMVC, его нужно настроить через xml-файл<mvc:interceptors>
Информация об узле.
Срез Аспект
Обзор нарезки
По сравнению с фильтром, перехватчик может знать, на каком контроллере окончательно обработан запрос, отправленный пользователем, но у перехватчика есть и явный недостаток, то есть он не может получить параметры запроса и ответа после обработки контроллера. Так что нарезке есть где пригодиться.
реализация слайса
Реализация нарезки требует внимания@Aspect
,@Component
а также@Around
Подробнее об использовании этих трех аннотаций см. в официальной документации:портал
@Aspect
@Component
public class TimeAspect {
private static final Logger LOG = LoggerFactory.getLogger(TimeAspect.class);
@Around("execution(* me.ifight.controller.*.*(..))")
public Object handleControllerMethod(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
LOG.info("切片开始。。。");
long startTime = System.currentTimeMillis();
// 获取请求入参
Object[] args = proceedingJoinPoint.getArgs();
Arrays.stream(args).forEach(arg -> LOG.info("arg is {}", arg));
// 获取相应
Object response = proceedingJoinPoint.proceed();
long endTime = System.currentTimeMillis();
LOG.info("请求:{}, 耗时{}ms", proceedingJoinPoint.getSignature(), (endTime - startTime));
LOG.info("切片结束。。。");
return null;
}
}
Фильтр, перехватчик и порядок вызова слайсов
На следующем рисунке показана последовательность вызова трех фильтров Filter->Intercepto->Aspect->Controller. Напротив, порядок обработки исключений, выдаваемых контроллером, — изнутри наружу. Поэтому мы всегда определяем аннотацию@ControllerAdvice
Для единообразной обработки исключений, создаваемых контроллерами. Если однажды исключение@ControllerAdvice
Если обработано, позвоните перехватчикуafterCompletion
параметры методаException ex
пусто.
Разница между фильтром и перехватчиком
Наконец, необходимо рассказать о разнице между фильтрами и перехватчиками:
Filter | Interceptor | |
---|---|---|
Метод реализации | Фильтры основаны на обратных вызовах функций | Механизм отражения на основе Java |
Спецификация | Спецификация сервлета | Спецификация пружины |
Сфера | Работает почти со всеми запросами | Работает только с запросами действий |
Кроме того, по сравнению с фильтрами, перехватчики могут «видеть», какой контроллер фреймворка Spring обрабатывает запрос пользователя.