Skip to content

Auto-configuration of MethodValidationPostProcessor prevents the use of @Validated on final @ConfigurationProperties classes #21454

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wilkinsona opened this issue May 14, 2020 · 6 comments
Assignees
Labels
type: enhancement A general enhancement
Milestone

Comments

@wilkinsona
Copy link
Member

This was raised by Philipp Paland on Gitter when trying to use records for @ConfigurationProperties. I believe it will apply to any final @ConfigurationProperties class that has @Validated on it.

While we don't require a proxy to perform configuration property validation, MethodValidationPostProcessor finds @Validated on the bean and tries to create a proxy. When the configuration property class is final, this fails. You can work around the problem by excluding ValidationAutoConfiguration and just declaring a validator:

package com.example.recordvalidation;

import javax.validation.constraints.NotBlank;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
import org.springframework.boot.context.properties.ConstructorBinding;
import org.springframework.boot.validation.MessageInterpolatorFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Role;
import org.springframework.validation.annotation.Validated;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;

@ConfigurationPropertiesScan
@SpringBootApplication(exclude = org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration.class)
public class RecordValidationApplication {

	public static void main(String[] args) {
		SpringApplication.run(RecordValidationApplication.class, args);
	}

	@Bean
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public static LocalValidatorFactoryBean defaultValidator() {
		LocalValidatorFactoryBean factoryBean = new LocalValidatorFactoryBean();
		MessageInterpolatorFactory interpolatorFactory = new MessageInterpolatorFactory();
		factoryBean.setMessageInterpolator(interpolatorFactory.getObject());
		return factoryBean;
	}

	@ConfigurationProperties(prefix = "myprops")
	@ConstructorBinding
	@Validated
	public record MyProps(@NotBlank String baseUrl) {}

}

This retains configuration property validation at the cost of method validation anywhere in the app. It would be nice if we could somehow keep both and just have the final @ConfigurationProperties class excluded from method validation.

@wilkinsona wilkinsona added for: team-attention An issue we'd like other members of the team to review status: waiting-for-triage An issue we've not yet triaged labels May 14, 2020
@philipp-paland
Copy link

Thanks for providing the workaround :)

@snicoll
Copy link
Member

snicoll commented Jun 3, 2020

After some brainstorming with @jhoeller, it looks like we unfortunately created an additional use-case for @Validated that overlaps with the ones of the core framework, that is:

  • @Validated at class-level to trigger method validation (the post-processor is optional but auto-configured in Spring Boot)
  • @Validated at parameter-level to trigger validation, typically to validate input arguments in MVC

Our binder looks for @Validated at class-level and triggers validation that looks more like the second use case and definitely not the first one.

There are a few workarounds to be considered:

  • Use a @ConfigurationProperties-specific way to trigger validation (breaking change)
  • Auto-configure an extension of MethodValidationPostProcessor that overrides isEligible and ignores @ConfigurationProperties-annotated types by default

@philwebb philwebb self-assigned this Jun 5, 2020
@philwebb philwebb added type: enhancement A general enhancement and removed for: team-attention An issue we'd like other members of the team to review status: waiting-for-triage An issue we've not yet triaged labels Jun 5, 2020
@philwebb philwebb added this to the 2.4.x milestone Jun 5, 2020
@snicoll

This comment has been minimized.

@wilkinsona
Copy link
Member Author

My recollection is that we discussed that approach, but decided against it as it would be inconsistent with how users expect to enable validation by using @Validated. Instead, I think we decided to look at introducing an extension of MethodValidationPostProcessor that can ignore ConfigurationProperties-annotated types. To avoid the post-processor having knowledge of @ConfigurationProperties, @philwebb was keen to have come sort of pluggable filtering mechanism.

@philwebb
Copy link
Member

philwebb commented Jun 8, 2020

My recollection is the same as @wilkinsona

@Thrillpool
Copy link

Thrillpool commented Nov 28, 2024

Noting for the convenience of people googling errors, as I just spent a couple of hours rediscovering this issue (or rather something similar) and solution, at which point I immediately found this github issue. this error may manifest as

Cause: java.lang.ClassCastException: class SomeClassOfYours$$SpringCGLIB$$0 cannot be cast to class org.springframework.cglib.proxy.Factory (SomeClassOfYours$$SpringCGLIB$$0 and org.springframework.cglib.proxy.Factory are in unnamed module of loader 'app')

And the same workaround resolves things

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: enhancement A general enhancement
Projects
None yet
Development

No branches or pull requests

5 participants