WebDev Portfolio

Middleware in Java

December 12, 2025

Middleware is one of those topics that sounds more mysterious than it actually is. In Java, it usually refers to the layer of code that sits between the incoming request and the core logic of your application. It’s the place where you handle the things every request needs, but your business logic shouldn’t have to worry about.

If you’ve worked with frameworks like Spring Boot, you’ve already used middleware even if you didn’t call it that. Filters, interceptors, and aspects all fall under the same general idea: code that runs before or after your main controller logic.

1. Servlet Filters

Logging is a classic case for middleware. Rather than sprinkling print statements across your codebase, a Filter can record request details in one place. Filters sit at the very edge of your application, catching requests before they even reach the Spring DispatcherServlet.

@Component
public class RequestLoggingFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 
            throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) request;

        // Logic before the request reaches the controller
        System.out.println("Incoming Request: " + httpRequest.getMethod() + " " + httpRequest.getRequestURI());

        chain.doFilter(request, response); // Pass the request to the next entity

        // Logic after the request is processed
        System.out.println("Response sent successfully.");
    }
}

2. Handler Interceptors

A common example is authentication. You don’t want every controller method to manually check tokens or session data. Instead, you can use a HandlerInterceptor. Interceptors are more "Spring-aware" than filters, giving you access to the specific handler (controller) that is about to execute.

@Component
public class AuthInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String authToken = request.getHeader("Authorization");

        if (authToken == null || !authToken.startsWith("Bearer ")) {
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            return false; // Stop the request execution here
        }
        return true; // Continue to the controller
    }
}

3. Aspect-Oriented Programming (AOP)

For even more granular control—like measuring how long a specific method takes to run—you might use an Aspect. This allows you to "wrap" logic around specific method calls based on annotations or naming patterns.

@Aspect
@Component
public class PerformanceAspect {
    @Around("execution(* com.example.service.*.*(..))")
    public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();

        Object proceed = joinPoint.proceed(); // Execute the actual method

        long executionTime = System.currentTimeMillis() - start;
        System.out.println(joinPoint.getSignature() + " executed in " + executionTime + "ms");
        return proceed;
    }
}

The real value of middleware is separation of concerns. Your controllers stay focused on what they’re supposed to do, and the cross‑cutting concerns: logging, security, metrics, validation, and more live in their own layer. It keeps things cleaner, easier to test, and easier to reason about.

Java gives you a few different ways to implement middleware depending on the framework you’re using, but the idea stays the same: handle the shared work once, and let the rest of your application stay focused on the actual problem you’re solving.