#
Filters & Interceptors in Spring
This tutorial explains what the filters & interceptors are and how they are used in Spring.
Please take a look at the following examples and read carefully the comments. The code is self-explanatory.
This example is created from a simple Spring Boot application created with Spring Initializr. I am using Maven, Java 17, Spring Boot 3.1.0. I have also added "Spring Web" and "Lombok" dependencies.
Starting from the base application downloaded from Spring Initializr, I updated the main class and added the more classes as below:
package com.example.demo;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class Employee {
private String id;
private String name;
private String department;
private int age;
public Employee(String id, String name, String department, int age) {
this.id = id;
this.name = name;
this.department = department;
this.age = age;
}
}
package com.example.demo;
import com.example.demo.filters.SecurityFilter;
import com.google.gson.Gson;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
@RestController
// Define a REST controller for testing our cases
public class EmployeeController {
Logger Logger = LoggerFactory.getLogger(SecurityFilter.class);
@Autowired
EmployeeService employeeService;
@GetMapping("/employee/all")
String getAllEmployees() {
ArrayList<Employee> allEmployees = employeeService.getEmployees();
String json = new Gson().toJson(allEmployees);
return json;
}
@PutMapping(value="/employee/add", consumes = "application/json")
int addEmployee(@RequestBody Employee newEmployee) {
Logger.info("Employee Controller - IN");
employeeService.addEmployee(newEmployee);
int newCount = employeeService.countEmployees();
Logger.info("Employee Controller - OUT");
return newCount;
}
@DeleteMapping(value="/employee/delete", consumes = "application/json")
int deleteEmployee(@RequestBody String empId) {
employeeService.removeEmployee(empId);
int newCount = employeeService.countEmployees();
return newCount;
}
}
Also, we need to add in pom.xml the following dependency:
<!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.10.1</version>
</dependency>
package com.example.demo;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.HashMap;
@Service
public class EmployeeService {
private HashMap<String, Employee> employees = new HashMap<>();
public void addEmployee(Employee Emp) {
this.employees.put(Emp.getId(), Emp);
}
public void removeEmployee(String empId) {
this.employees.remove(empId);
}
public int countEmployees() {
return this.employees.size();
}
public ArrayList<Employee> getEmployees() {
ArrayList<Employee> empList = new ArrayList<>();
// go through the values of the map
for (Employee emp : employees.values()) {
empList.add(emp);
}
return empList;
}
}
package com.example.demo.filters;
import jakarta.servlet.*;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
@WebFilter()
// Define a filter for logging
public class LoggingFilter implements Filter {
Logger Logger = LoggerFactory.getLogger(LoggingFilter.class);
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest r1 = (HttpServletRequest) request;
Logger.info("LoggingFilter// before doFilter() for URL req. = " + r1.getRequestURI());
chain.doFilter(request, response);
Logger.info("LoggingFilter// AFTER doFilter() for URL req. = " + r1.getRequestURI());
}
}
package com.example.demo.filters;
import jakarta.servlet.*;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
@WebFilter()
// Define another filter for testing the chaining of the filters
public class SecurityFilter implements Filter {
Logger Logger = LoggerFactory.getLogger(SecurityFilter.class);
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest r1 = (HttpServletRequest) request;
Logger.info("SecurityFilter// before doFilter() for URL req. = " + r1.getRequestURI());
chain.doFilter(request, response);
Logger.info("SecurityFilter// AFTER doFilter() for URL req. = " + r1.getRequestURI());
}
}
package com.example.demo.interceptors;
import com.example.demo.filters.SecurityFilter;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import java.util.UUID;
// Implements preHandle(), postHandle() and afterCompletion() methods of HandlerInterceptor interface
public class LoggingInterceptor implements HandlerInterceptor {
Logger log = LoggerFactory.getLogger(SecurityFilter.class);
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
UUID uuid = UUID.randomUUID();
request.setAttribute("request-id", uuid );
log.info(">>>> preHandle() for "+request.getRequestURI());
// when "true" we allow user to access rest controller api
return true;
}
@Override
public void postHandle( HttpServletRequest request,
HttpServletResponse response,
Object handler,
ModelAndView modelAndView) throws Exception {
log.info(">>>> postHandle() for "+request.getRequestURI()+" ATTR(request-id)= "+request.getAttribute("request-id"));
}
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception exception) throws Exception {
log.info(">>>> afterCompletion() for " + request.getRequestURI() + " ATTR(request-id)= " + request.getAttribute("request-id"));
}
}
package com.example.demo.components;
import com.example.demo.interceptors.LoggingInterceptor;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Component
public class RegisterInterceptors implements WebMvcConfigurer {
@Override
// Register the LoggingInterceptor
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor( new LoggingInterceptor() );
}
}
package com.example.demo;
import com.example.demo.filters.LoggingFilter;
import com.example.demo.filters.SecurityFilter;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import java.util.concurrent.ExecutionException;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) throws InterruptedException, ExecutionException {
ApplicationContext appContext = SpringApplication.run(DemoApplication.class, args);
}
@Bean
// Setting the proprieties of SecurityFilter
public FilterRegistrationBean<SecurityFilter> filterSecurityBean() {
FilterRegistrationBean<SecurityFilter> filterPropertiesBean = new FilterRegistrationBean();
filterPropertiesBean.setFilter(new SecurityFilter());
filterPropertiesBean.addUrlPatterns("/employee/*");
filterPropertiesBean.setOrder(2);
return filterPropertiesBean;
}
@Bean
// Setting the proprieties of LoggingFilter
public FilterRegistrationBean<LoggingFilter> filterLoggingBean() {
FilterRegistrationBean<LoggingFilter> filterPropertiesBean = new FilterRegistrationBean();
filterPropertiesBean.setFilter(new LoggingFilter());
filterPropertiesBean.addUrlPatterns("/employee/all");
filterPropertiesBean.setOrder(1);
return filterPropertiesBean;
}
}
When we send the "/employee/add" request we will see something like that in the logs :
c.example.demo.filters.SecurityFilter : SecurityFilter// before doFilter() for URL req. = /employee/add
c.example.demo.filters.SecurityFilter : >>>> preHandle() for /employee/add
c.example.demo.filters.SecurityFilter : Employee Controller - IN
c.example.demo.filters.SecurityFilter : Employee Controller - OUT
c.example.demo.filters.SecurityFilter : >>>> postHandle() for /employee/add ATTR(request-id)= d296eafe-71ec-4a0a-8a5a-84b5c593a91b
c.example.demo.filters.SecurityFilter : >>>> afterCompletion() for /employee/add ATTR(request-id)= d296eafe-71ec-4a0a-8a5a-84b5c593a91b
c.example.demo.filters.SecurityFilter : SecurityFilter// AFTER doFilter() for URL req. = /employee/add
When we send the "/employee/all" request we will see something like that in the logs :
com.example.demo.filters.LoggingFilter : LoggingFilter// before doFilter() for URL req. = /employee/all
c.example.demo.filters.SecurityFilter : SecurityFilter// before doFilter() for URL req. = /employee/all
c.example.demo.filters.SecurityFilter : >>>> preHandle() for /employee/all
c.example.demo.filters.SecurityFilter : >>>> postHandle() for /employee/all ATTR(request-id)= d113f7cd-3ef1-46eb-98ee-b01a4b53a0f8
c.example.demo.filters.SecurityFilter : >>>> afterCompletion() for /employee/all ATTR(request-id)= d113f7cd-3ef1-46eb-98ee-b01a4b53a0f8
c.example.demo.filters.SecurityFilter : SecurityFilter// AFTER doFilter() for URL req. = /employee/all
com.example.demo.filters.LoggingFilter : LoggingFilter// AFTER doFilter() for URL req. = /employee/all
To understand better the difference from Filters & Interceptors in Spring take a look at the following picture:
Remarks:
- Using Filters and Interceptors, we can handle a request/response at different levels. This could be seen in the code/logs above.
- By default, the Filters apply to all requests. If we want to define which requests the filter apply, we need to create
Beans for each filter by using
FilterRegistrationBean
class. This could be done also with @WebFilter() annotation. - The order of the Filters is defined in the FilterRegistrationBean beans if exists, otherwise, we can use
@Order
annotation. - In order to create an Interceptor, we need to implement
HandlerInterceptor
interface. - To "activate" an interceptor, we need to register the interceptor with Spring, by implementing
WebMvcConfigurer
interface. HandlerInterceptor
interface has 3 methods to implement:preHandle()
(called before the execution of the actual handler),postHandle()
(called after the execution of the actual handler, but before the response is sent to the client),afterCompletion()
(called after the response is sent to the client).