Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Interceptor в Java
Определение
Interceptor — это паттерн проектирования, который позволяет обработать (перехватить) запрос до попадания в целевой объект или после получения ответа от него. Interceptor позволяет добавлять функциональность без изменения исходного кода.
Существуют Interceptors в разных контекстах Java:
- HTTP Interceptors (OkHttp, Spring)
- Servlet Filters (J2EE)
- AOP Proxies (Spring AOP)
- Handlers в HTTP клиентах
HTTP Interceptor в OkHttp
OkHttp — популярная библиотека для HTTP запросов, имеет встроенный Interceptor интерфейс:
public interface Interceptor {
Response intercept(Chain chain) throws IOException;
interface Chain {
Request request();
Response proceed(Request request) throws IOException;
}
}
Пример: Логирование всех HTTP запросов
public class LoggingInterceptor implements Interceptor {
private static final Logger logger = LoggerFactory.getLogger(LoggingInterceptor.class);
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
long startTime = System.nanoTime();
logger.info("Sending request: {} {}", request.method(), request.url());
// Печатаем headers запроса
for (String header : request.headers().names()) {
logger.info("{}: {}", header, request.header(header));
}
try {
Response response = chain.proceed(request);
long endTime = System.nanoTime();
long duration = (endTime - startTime) / 1000000;
logger.info("Received response: {} in {}ms",
response.code(), duration);
logger.info("Content-Type: {}", response.header("content-type"));
return response;
} catch (IOException e) {
long endTime = System.nanoTime();
long duration = (endTime - startTime) / 1000000;
logger.error("Request failed: {} in {}ms",
request.url(), duration, e);
throw e;
}
}
}
// Использование
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(new LoggingInterceptor())
.build();
Request request = new Request.Builder()
.url("https://api.example.com/users")
.build();
Response response = client.newCall(request).execute();
HTTP Interceptor в Spring RestTemplate
Spring предоставляет ClientHttpRequestInterceptor для перехвата запросов:
public class AuthTokenInterceptor implements ClientHttpRequestInterceptor {
private final TokenService tokenService;
public AuthTokenInterceptor(TokenService tokenService) {
this.tokenService = tokenService;
}
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body,
ClientHttpRequestExecution execution) throws IOException {
// Получаем токен и добавляем в заголовок
String token = tokenService.getAccessToken();
request.getHeaders().setBearerAuth(token);
// Продолжаем с модифицированным запросом
ClientHttpResponse response = execution.execute(request, body);
// Обработка ответа
if (response.getStatusCode() == HttpStatus.UNAUTHORIZED) {
// Можем обновить токен и повторить запрос
token = tokenService.refreshToken();
request.getHeaders().setBearerAuth(token);
response = execution.execute(request, body);
}
return response;
}
}
// Конфигурация
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate(TokenService tokenService) {
RestTemplate restTemplate = new RestTemplate();
restTemplate.getInterceptors().add(new AuthTokenInterceptor(tokenService));
return restTemplate;
}
}
Interceptor в Spring MVC (Servlet)
Spring MVC имеет HandlerInterceptor для перехвата HTTP запросов на уровне контроллера:
public class RequestTimingInterceptor implements HandlerInterceptor {
private static final Logger logger = LoggerFactory.getLogger(RequestTimingInterceptor.class);
private static final String TIMER_KEY = "startTime";
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
// Выполняется ДО вызова контроллера
long startTime = System.currentTimeMillis();
request.setAttribute(TIMER_KEY, startTime);
logger.info("Request started: {} {}", request.getMethod(), request.getRequestURI());
return true; // true = продолжить обработку, false = остановить
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response,
Object handler, ModelAndView modelAndView) throws Exception {
// Выполняется ПОСЛЕ вызова контроллера, но ДО отправки ответа
long startTime = (Long) request.getAttribute(TIMER_KEY);
long duration = System.currentTimeMillis() - startTime;
logger.info("Request processed in {}ms", duration);
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) throws Exception {
// Выполняется ПОСЛЕ отправки ответа клиенту
if (ex != null) {
logger.error("Request failed with exception", ex);
}
}
}
// Регистрация
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new RequestTimingInterceptor())
.addPathPatterns("/api/**")
.excludePathPatterns("/api/public/**");
}
}
Цепочка Interceptors
Несколько Interceptors могут быть применены последовательно:
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(new LoggingInterceptor()) // 1
.addInterceptor(new AuthInterceptor()) // 2
.addInterceptor(new RetryInterceptor()) // 3
.addNetworkInterceptor(new CompressionInterceptor()) // Сетевой уровень
.build();
// Порядок выполнения:
// Request: LoggingInterceptor → AuthInterceptor → RetryInterceptor → Network → Server
// Response: Network → RetryInterceptor → AuthInterceptor → LoggingInterceptor → Client
Практический пример: Retry Interceptor
public class RetryInterceptor implements Interceptor {
private static final int MAX_RETRY = 3;
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
IOException exception = null;
for (int attempt = 0; attempt < MAX_RETRY; attempt++) {
try {
return chain.proceed(request);
} catch (IOException e) {
exception = e;
long backoff = 100 * (long) Math.pow(2, attempt);
System.out.println("Attempt " + (attempt + 1) + " failed, retrying in " + backoff + "ms");
try {
Thread.sleep(backoff);
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
throw new IOException(ie);
}
}
}
throw exception;
}
}
Различия между типами Interceptors
OkHttp Interceptor vs Network Interceptor:
// Application Interceptor
.addInterceptor(interceptor)
// - Может модифицировать request/response
// - Видит только финальный ответ (кэш, редиректы уже обработаны)
// - Не повторяется при редиректах
// Network Interceptor
.addNetworkInterceptor(interceptor)
// - Работает ближе к сети
// - Видит все промежуточные запросы (редиректы)
// - Срабатывает для каждого сетевого запроса
Interceptor в Spring WebFlux (Reactive)
@Component
public class LoggingWebFilter implements WebFilter {
private static final Logger logger = LoggerFactory.getLogger(LoggingWebFilter.class);
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
long startTime = System.currentTimeMillis();
ServerHttpRequest request = exchange.getRequest();
logger.info("Incoming {} {}", request.getMethod(), request.getPath());
return chain.filter(exchange)
.doFinally(signalType -> {
long duration = System.currentTimeMillis() - startTime;
logger.info("Completed in {}ms", duration);
});
}
}
Лучшие практики использования Interceptors
- Логирование — записывайте все запросы и ответы
- Аутентификация — добавляйте токены в headers
- Повторные попытки — обработка временных ошибок
- Трансформация ответов — парсинг, кэширование
- Error handling — перехват и обработка ошибок
- Performance monitoring — измерение времени запросов
Выводы
Interceptor позволяет:
- Добавлять функциональность без изменения исходного кода
- Разделять concerns — логирование, аутентификация, трансформация отделены
- Переиспользовать код — один Interceptor можно применить ко многим запросам
- Обрабатывать cross-cutting concerns — функциональность, пересекающая несколько слоёв
Это мощный паттерн, особенно при работе с HTTP запросами и сетевыми операциями.