DEV Community

Cover image for How not to make a framework for default request values in Spring
Artem Ptushkin
Artem Ptushkin

Posted on • Updated on

How not to make a framework for default request values in Spring

I used to look for common solutions for the problems I face. And I fail sometimes within my researches of the best solution like many developers. But it's still important to describe your path for the future researcher.


There are a lot of written Spring REST API services with a lot of common code lines like converters, formatters, custom marshallers, etc. But it is a question still:

How to make dynamic Spring default values for composite objects input into a REST controller?

There are a lot of questions and discussions about it [1]

What do we have?

  1. Default values for request parameters and request headers

    @RequestParam(defaultValue = "19") Integer age,
    @RequestHeader(defaultValue = "John Snow") String name
  2. Dynamic values for request parameters and request headers

    @RequestParam(defaultValue = "${}") Integer size,
    @RequestHeader(defaultValue = "${}") String name
  3. Query parameters collection into a custom object. It doesnt require RequestParam annotation and doesnt work with headers by default

    public Character getCharacter(Character character)

    DTO class:

    public class Character {
        private String name;
        private Integer age;

    Example call:

    curl --location --request GET 'http://localhost:8080/characters? age=19&name=John%20Snow'
  4. Request body collection into a custom object

    public Character getCharacter(@RequestBody Character character)

In this way, we can customize default values or dynamic one by Spring properties in cases of:

Default values Dynamic default values
Composite query TRUE FALSE
Composite header TRUE FALSE

What do we need?

The most rational way is to have a mechanism to put default values by annotation/annotation's parameters as we do with default values in annotations: @Value, @RequestParam, @RequestHeader


Documentation: It is possible that in future this annotation could be used for value defaulting, and especially for default values of Creator properties, since they support required() in 2.6 and above.

  • Spring must provide custom properties to Jackson module. For example:
        name: Artem
Enter fullscreen mode Exit fullscreen mode

Workaround with a decorator class

There is a workaround for single cases to have an object of default values.
The hard part that we have to verify nullable values at every field and then use a field from another one.

  1. Have a common interface because we'll write a wrapper:

    public interface Unit {
        String getName();
        Long getId();
  2. Request body implementation class:

    public class UnitRequest implements Unit {
        private String name;
        private Long id;
  3. Declare default properties:

            name: Artem
  4. Declare a class for default properties:

    public class DefaultProperties<T> {
        private T value;
  5. Declare a configuration properties bean. Please notice, that I suggest to reuse the same class UnitRequest for properties because it is the most rational way to reuse existed class behavior. The other way - create another implementation:

    public class DefaultPropertiesConfiguration {
        public DefaultProperties<UnitRequest> unitRequestDefaultProperties() {
            return new DefaultProperties<>();
  6. Create a wrapper class to use values from another object on null cases:

    public class UnitRequestWrapper implements Unit {
        private final Unit decorated;
        private final Unit defaultUnit;
        public UnitRequestWrapper(Unit decorated, Unit defaultUnit) {
            this.decorated = decorated;
            this.defaultUnit = defaultUnit;
        public String getName() {
            String actual = decorated.getName();
            return actual == null ? defaultUnit.getName() : actual;
        public Long getId() {
            Long actual = decorated.getId();
            return actual == null ? defaultUnit.getId() : actual;
  7. Change your RestController code

    private final DefaultProperties<UnitRequest> unitRequestDefaultProperties; //custom
    public UnitResponse post(@RequestBody UnitRequest unitRequest) {
        Unit unit = new UnitRequestWrapper(unitRequest, unitRequestDefaultProperties.getValue()); //custom
        return UnitResponse

Workaround with a proxy class

This solution is very close to a reusable one and could be a part of another solution.

  1. Proxy methods of web request DTO to call the methods from another object on null values:

    public class NullableProxyFactory {
    public <T> T getProxy(Class<? super T> type, T first, T second) {
        return (T) Proxy.newProxyInstance(
                new Class[]{type},
                new NullableInvocationHandler<>(first, second)
  2. Invocation Handler code:

    public class NullableInvocationHandler<T> implements InvocationHandler {
        private final T first;
        private final T second;
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            Method m = findMethod(second.getClass(), method);
            if (m != null) {
                Object invocationResult = m.invoke(first, args);
                if (invocationResult == null) {
                    return m.invoke(second, args);
                return invocationResult;
            return null;
        private Method findMethod(Class<?> clazz, Method method) {
            try {
                return clazz.getDeclaredMethod(method.getName(), method.getParameterTypes());
            } catch (NoSuchMethodException e) {
                return null;
  3. Usage:

        public UnitResponse post(@RequestBody UnitRequest unitRequest) {
            Unit unit = proxyFactory.getProxy(Unit.class, unitRequest, unitRequestDefaultProperties.getValue());


  1. We have only one additional line inside the controller
  2. The Proxy solution looks interesting for reusable cases. We could not write boilerplate code for field values verification.
  3. We can take inside a library three classes from the proxy example.


  1. It will take many more lines of code to proxy the value of the internal composite fields. Like when UnitRequest has a field SubUnit.
  2. It requires modifying the controller code and this is redundant cause we have a lot of Spring and Jackson code from above.
  3. We have to write boilerplate code inside our wrapper.
  4. The Proxy example is somehow applicable to dynamic query parameters and dynamic headers but the Wrapper example not so much.

What I have tried also?

  • AOP. I've written an aspect to interact with all the calls for getter methods with my custom annotation. The annotation is needed to reduce method quantity. But there is a complicated moment with Spring bean injection into aspect. You have to put @Configurable on every DTO class plus AOP code seemed too risky, I've got a lot of problems on this way.
  • @ControllerAdvice + @InitBinder. It works for single cases also. You either override all the fields or have to verify (as in my workaround above) all the fields values
  • Proxy + BeanDeserializerModifier. It is possible to wrap the result of Jackson deserialization method. I have wrapped the result by proxy object but got the java.lang.IllegalArgumentException: argument type mismatch exception. The Proxy type object came to the controller it is obviously unresolvable.


I wanted to share the unsuccessful example of software solution research in Java backend world. It helps me to understand better the Jackson library and Proxy mechanisms.

Keep researching the best solution. It broadens the mind on the way.

Hope this article describes the question fully.

Problem links


Top comments (0)