3

I have a simple controller with method test:

 @RequestMapping(produces = "application/json")
    @ResponseBody
    public HttpEntity<Void> test(Test test) {
        return new ResponseEntity<>(HttpStatus.OK);
    }

Test class looks like this:

    public class Test {
    private String name;
    private Date date;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Date getDate() {
        return date;
    }

    @DateTimeFormat(iso= DateTimeFormat.ISO.DATE)
    public void setDate(Date date) {
        this.date = date;
    }
}

And I need default values for fields of Test object. If I had a primitive param, I would be able to use @RequestParam(required = false, defaultValue = "someValue"). But with non-primitive param this approach doesn't seem to work. I see a couple of variants how to deal with it:

  • Assign values in a constructor. Not very good, because may be I will need different defaults for different methods.
  • Write custom DataBinder. Better, but the problem with different defaults still exists.
  • Write custom DataBinder and custom annotation with defaults.

Am I missing something and there is a built in feature which can solve my problem?

Artem Malinko
  • 1,761
  • 1
  • 22
  • 39
  • 1
    Why can't you assign the default value in the method itself, after checking it is `null`? – Rohit Jain Apr 06 '15 at 11:00
  • It's not very convenient. In each method where I have defaults I need to write some ifs and assignments. While with primitive types it is handled via annotation. – Artem Malinko Apr 06 '15 at 11:52

1 Answers1

1

You can lean on argument resolving, in four easy steps, similiar as you suggested in your third point.

  1. Create an annotation e.g.

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TestDefaultValues {
    String[] value();
}
  1. Write a resolver e.g.

public class TestArgumentResolver implements HandlerMethodArgumentResolver {

    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.getParameterAnnotation(TestDefaultValues.class) != null;
    }

    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest,
            WebDataBinderFactory binderFactory) throws Exception {
        TestDefaultValues attr = parameter.getParameterAnnotation(TestDefaultValues.class);
        String[] value = attr.value();
        Test test = new Test();
        test.setName(value[0]);
        test.setDate(new Date(value[1]));
        return test;
    }

}
  1. register a resolver

<mvc:annotation-driven>
        <mvc:argument-resolvers>
            <bean class="your.package.TestArgumentResolver"></bean>
        </mvc:argument-resolvers>
</mvc:annotation-driven>
  1. use an annotation in your controller method e.g.

 public HttpEntity<Void> test(@TestDefaultValues({"foo","11/12/2014"}) Test test) {

instantiating date is just to get the gist of the implementation, obviously you'll use whatever is your idea

Master Slave
  • 27,771
  • 4
  • 57
  • 55
  • Thank you for your answer. It is better, but still we tied to Test class. I hoped there is a generic annotation for this purpose in Spring, but it seems like there is not. – Artem Malinko Apr 07 '15 at 07:09
  • You welcome. In fact, depending on your needs, but you can use this approach and make it more generic, note that the _resolveArgument_ method returns _Object_. For example you can use the following signature `(@TestDefaultValues({"foo","11/12/2014", "test"}) Object test)` and than in the resolver class use the additional annotation parameter to figure out which class you're instantiating, and other annotation params to populate the default values. Anyways the only thing that matters is that you know the construct, you'll decide if you can use it for what you're trying to achieve – Master Slave Apr 07 '15 at 07:51