1

I want to make my query param class immutable (with public final fields set via constructor). Is there a way to enforce SearchQueryParam instance creation via constructor and do not expose dreadful getters/setters?

Here is sample code which works:

@RequestMapping(value = "/search", method = GET, produces = APPLICATION_JSON_VALUE)
public List<Result> search(SearchQueryParam searchQueryParam) {
        //do stuff;
}


public class SearchQueryParam {
    @DateTimeFormat(iso = DATE_TIME)
    private DateTime from;
    @DateTimeFormat(iso = DATE_TIME)
    private DateTime to;

    public DateTime getFrom() {
        return from;
    }

    public void setFrom(DateTime from) {
        this.from = from;
    }

    public DateTime getTo() {
        return to;
    }

    public void setTo(DateTime to) {
        this.to = to;
    }
}

but I would like my SearchQueryParam class look more like this:

public final class SearchQueryParam {

    @DateTimeFormat(iso = DATE_TIME)
    public final DateTime from;
    @DateTimeFormat(iso = DATE_TIME)
    public final DateTime to;

    public SearchQueryParam(DateTime from, DateTime to) {
        this.from = from;
        this.to = to;
    }
}
Łukasz
  • 206
  • 1
  • 7
  • Not sure if I understood your question: if you declare properties inside your SearchQueryParam as public (as you did in your own example) then you can just access its values without needing getters. And since you also declared them as "final", you will not be able to change values after creation. So what is the problem with your own example? – mapm Jun 03 '16 at 12:13
  • What's preventing you to use the second version of `SearchQueryParam` ? – Spotted Jun 03 '16 at 13:37
  • In second case Spring can not create SearchQueryParam due to lack of default constructor. Want I want here is to enforce Spring to use SearchQueryParam(DateTime, DateTime) constructor. – Łukasz Jun 04 '16 at 17:12

2 Answers2

1

You can use WebArgumentResolver for that. Fist of all you must write your own resolver like this

public class MySpecialArgumentResolver implements WebArgumentResolver {

   public Object resolveArgument(MethodParameter methodParameter, NativeWebRequest webRequest) {
     if (methodParameter.getParameterType().equals(MySpecialArg.class)) {
       return new MySpecialArg("myValue");
     }
     return UNRESOLVED;
   }
 }

after that you need to register this resolver in spring

<mvc:annotation-driven>
<mvc:argument-resolvers>
    <bean id="mySpecialArgumentResolver" class="..MySpecialArgumentResolver ">
    </bean>            
</mvc:argument-resolvers>

you can read Use immutable objects in your Spring MVC controller by implementing your own WebArgumentResolver for more details. In this post used old style, after spring 3.1 they added HandlerMethodArgumentResolver and you can use it. Here example for new version stackoverflow post

Community
  • 1
  • 1
Sergey
  • 310
  • 2
  • 7
0

Please try below code

public final class SearchQueryParam {

@DateTimeFormat(iso = DATE_TIME)
private final DateTime from;
@DateTimeFormat(iso = DATE_TIME)
private final DateTime to;

public SearchQueryParam(DateTime from, DateTime to) {
    this.from = from;
    this.to = to;
}

public DateTime getFrom() {
    return this.from;
}

public DateTime getTo() {
    return this.to;
}

}

Below are the few comments which will help you to understand steps for making class as immutable

  • No need to clone the input DateTime parameters (Assuming you are using Joda library, in case of Joda DateTime objects are immutable, you don't need to clone them)
  • Made from, to fields as final so that those can be set only from constructor
kedar kamthe
  • 8,048
  • 10
  • 34
  • 46