Reputation: 4338
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
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