wst
wst

Reputation: 4338

SpringBoot validation of a bean method parameters and return

I can't make method level validation right. Or I don't understand how it works.

My application class is below. Very simple. It contains MethodValidationPostProcessor bean definition. It also runs Greeter service.

@SpringBootApplication
public class App implements CommandLineRunner {
    private final Greeter greeter;

    public App(Greeter greeter) {
        this.greeter = greeter;
    }

    public static void main(String[] args) {
        new SpringApplicationBuilder().main(App.class).sources(App.class).web(false).run(args).close();
    }

    @Bean
    public org.springframework.validation.beanvalidation.MethodValidationPostProcessor methodValidationPostProcessor() {
        return new MethodValidationPostProcessor();
    }

    @Override
    public void run(String... args) throws Exception {
        final Input input = new Input();
        input.setName("j");
        final String messageFromInput = greeter.getMessageFromInput(input);

        final String messageFromString = greeter.getMessageFromString("j");
    }
}

Greeter service below. Here I do expect to validate input and output.

@Service
@Validated
public class Greeter {
    String getMessageFromInput(@Valid @NotNull Input name) {
        return "[From Input] Greetings! Oh mighty " + name + "!";
    }

    String getMessageFromString(@Size(min = 4) String name) {
        return "[From String] Greetings! Oh mighty " + name + "!";
    }
}

Input DTO is very simple as well.

public class Input {
    @NotEmpty
    @Size(min = 3)
    private String name;

    // Getters, setters and toString ommited.
}

Since the name in both cases, direct String and DTO, is only one letter I expect this setup to throw exception. Unfortunately, nothing happens and application completes successfully. It works with controller's methods. But I would like it to work with any bean's methods.

Upvotes: 0

Views: 2136

Answers (1)

M. Deinum
M. Deinum

Reputation: 124471

You are injecting your Greeter bean as a constructor argument into the class annotated with @SpringBootApplication which is a @Configuration class. To satisfy that dependency the Greeter is created very early on in the startup process of the ApplicationContext and as such will remove it as a candidate for proxy creation.

Instead of injecting it as a constructor argument move your CommandLineRunner logic to a @Bean annotated method and simply inject the Greeter as a dependency. This will delay the creation of the bean and as such make it available for proxying.

@Bean
public CommandLineRunner runner(Greeter greeter) {
    return new CommandLineRunner() {

        @Override
        public void run(String... args) throws Exception {
            final Input input = new Input();
            input.setName("j");
            final String messageFromInput = greeter.getMessageFromInput(input);

            final String messageFromString = greeter.getMessageFromString("j");
        }
    };
}

Another thing is that your methods of the Greeter should be, due the the nature of proxies, public.

Upvotes: 2

Related Questions