Unlock Spring Boot’s Powerful Built‑In Features You’re Not Using

This article walks through Spring Boot’s built‑in utilities—including request logging with CommonsRequestLoggingFilter, request/response wrappers, the OncePerRequestFilter base class, and AOP helpers like AopContext, AopUtils, and ReflectionUtils—showing how to configure and apply them in real code.

Programmer XiaoFu
Programmer XiaoFu
Programmer XiaoFu
Unlock Spring Boot’s Powerful Built‑In Features You’re Not Using

Request Data Logging

Spring Boot includes AbstractRequestLoggingFilter with the subclass CommonsRequestLoggingFilter. Defining a bean enables logging of query strings, payloads, headers, and client information, and allows a custom message prefix.

@Configuration
public class RequestLoggingConfig {
    @Bean
    public CommonsRequestLoggingFilter logFilter() {
        CommonsRequestLoggingFilter filter = new CommonsRequestLoggingFilter();
        filter.setIncludeQueryString(true);
        filter.setIncludePayload(true);
        filter.setIncludeHeaders(true);
        filter.setIncludeClientInfo(true);
        filter.setAfterMessagePrefix("REQUEST DATA-");
        return filter;
    }
}

Set the logger level to DEBUG for org.springframework.web.filter.CommonsRequestLoggingFilter to record detailed request information.

logging.level.org.springframework.web.filter.CommonsRequestLoggingFilter=DEBUG

Request/Response Wrappers

What they are

ContentCachingRequestWrapper

caches the request input stream, allowing multiple reads of the request body. ContentCachingResponseWrapper caches the response output stream, enabling modification before the response is committed.

Typical use cases

Request logging : record headers, parameters, and body before and after processing.

Request data modification : alter headers or body before the controller handles the request.

Response content modification : add or change headers, or sign the response body before it reaches the client.

Performance testing : cache request/response data to benchmark without affecting real network I/O.

Example: Request wrapper filter

import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Component
public class RequestWrapperFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain filterChain)
            throws ServletException, IOException {
        ContentCachingRequestWrapper requestWrapper = new ContentCachingRequestWrapper(request);
        // optional processing, e.g., logging
        byte[] body = requestWrapper.getContentAsByteArray();
        // log body ...
        filterChain.doFilter(requestWrapper, response);
    }
}

Example: Response wrapper filter

import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Component
public class ResponseWrapperFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain filterChain)
            throws ServletException, IOException {
        ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper(response);
        filterChain.doFilter(request, responseWrapper);

        // optional processing, e.g., adding a signature
        byte[] body = responseWrapper.getContentAsByteArray();
        responseWrapper.setHeader("X-Signature", "some-signature");
        responseWrapper.copyBodyToResponse();
    }
}
OncePerRequestFilter

guarantees that the filter logic runs only once per request lifecycle, preventing duplicate processing during forwards or includes.

OncePerRequestFilter

Spring‑provided base class that extends Filter with the following characteristics:

Single execution : ensures the filter runs only once per request, regardless of forwards or includes.

Built‑in support for request/response wrappers : simplifies caching and modification of request and response data.

Code simplification : developers inherit the class to avoid repetitive filter registration code.

Easy extensibility : overriding doFilterInternal lets developers add custom logic without handling registration or execution count.

Typical scenarios include request logging, request data modification, response alteration, security checks, performance monitoring, and exception handling, all executed exactly once per request.

AOP Utilities

AopContext

Provides access to the current proxy and its target object. Key methods: getTargetObject(): obtain the target object. currentProxy(): obtain the current proxy, useful for invoking another method in the same class that is annotated with @Transactional without losing proxy behavior.

public void noTransactionTask(String keyword) {
    ((YourClass) AopContext.currentProxy()).transactionTask(keyword);
}

@Transactional
void transactionTask(String keyword) {
    try {
        Thread.sleep(5000);
    } catch (InterruptedException e) {
        // handle exception
    }
    System.out.println(keyword);
}

AopUtils

Static helpers for AOP proxy inspection. getTargetObject(): retrieve the target from a proxy. isJdkDynamicProxy(Object): determine if the object is a JDK dynamic proxy. isCglibProxy(Object): determine if the object is a CGLIB proxy.

import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.aop.support.AopUtils;

public class AopUtilsExample {
    public static void main(String[] args) {
        MyService myService = ...; // assumed proxied
        if (AopUtils.isCglibProxy(myService)) {
            System.out.println("This is a CGLIB proxy object");
        }
    }
}

ReflectionUtils

Convenient methods for Java reflection, reducing boilerplate. makeAccessible(Field): make a private field accessible. getField(Field, Object): read a field value. invokeMethod(Method, Object, ...): invoke a method reflectively.

import org.springframework.util.ReflectionUtils;
import java.lang.reflect.Field;
import java.util.Map;

public class ReflectionUtilsExample {
    public static void main(String[] args) throws Exception {
        ExampleBean bean = new ExampleBean();
        bean.setMapAttribute(new HashMap<>());

        Field field = ReflectionUtils.findField(ExampleBean.class, "mapAttribute");
        ReflectionUtils.makeAccessible(field);
        Object value = ReflectionUtils.getField(field, bean);
        System.out.println(value);
    }

    static class ExampleBean {
        private Map<String, String> mapAttribute;
        public void setMapAttribute(Map<String, String> mapAttribute) {
            this.mapAttribute = mapAttribute;
        }
    }
}

These built‑in Spring components enable developers to add logging, modify requests/responses, enforce single‑execution filters, and work with AOP proxies without writing boilerplate code.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

backendAOPSpring BootOncePerRequestFilterContentCachingWrapperRequestLoggingFilter
Programmer XiaoFu
Written by

Programmer XiaoFu

xiaofucode.com – a programmer learning guide driven by the pursuit of profit

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.