问题
I'm using Bean Validation. I have a custom validator @MyValidator
that needs to look up a value with an injected Spring managed DAO object. How can I get access to this? Spring isn't injecting the DAO into my "MyValidator" object.
@Component
public class CodeListValidator implements ConstraintValidator<CodeList, String> {
@Autowired
private ICodeListCommonService codeListCommonService;
private CodeListEnum codeListID;
@Override
public void initialize(CodeList constraintAnnotation) {
this.codeListID = constraintAnnotation.value();
}
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
return codeListCommonService.doesCodeEntityExistForCodeList(codeListID.getDbCodeListId(), value, ProviderConstants.CODE_LIST_STATUS_ACTIVE);
}
}
The "codeListCommonService" is null. This is because Spring isn't creating the class - but how can I get this to work with Spring AoP?
The use of this validator looks like this:
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();
MyObject validateMe = new MyObject();
Set<ConstraintViolation<MyObject>> constraintViolations = validator.validate(validateMe);
For MyObject:
public class MyObject {
@Size(max = 1)
@CodeList(CodeListEnum.CARTYPE)
public String carType;
}
So when the validator runs, it processes the annotations... I just need to get a service injected into the CodeListValidator I made to it can do a DB lookup to verify the value against the DB list of "valid car type values".
EDIT: The solution:
Played around with the idea of making a Spring aware factory- too much integration with existing code.
The solution that seems the best (and it works here) is to make a Spring service that stores the ApplicationContext in a static method so "non-Spring managed" beans can get to them.
So a new service:
@Service
public class SpringApplicationContextService implements ISpringApplicationContextService, ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
}
And then any validator or non-Spring bean can get at the Spring beans via:
public class CodeListValidator implements ConstraintValidator<CodeList, String> {
@Autowired
private ICodeListCommonService codeListCommonService;
private CodeListEnum codeListID;
@Override
public void initialize(CodeList constraintAnnotation) {
this.codeListID = constraintAnnotation.value();
}
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
ICodeListCommonService codeListCommonService = SpringApplicationContextService.getApplicationContext().getBean(ICodeListCommonService.class);
return codeListCommonService.doesCodeEntityExistForCodeList(codeListID.getDbCodeListId(), value, ProviderConstants.CODE_LIST_STATUS_ACTIVE);
}
}
And, of course, the reason for all of this (which is used dozens of times in this app):
@CodeList(CodeListEnum.EMAIL_OPT_OUT_FLAG)
public String emailOptOutFlag;
@CodeList(CodeListEnum.CLEARING_HOUSE)
public String clearingHouse;
回答1:
The minimum setup for @Autowired
to work properly in ConstraintValidator
implementation is to have this bean in a Spring @Configuration
:
@Bean
public Validator defaultValidator() {
return new LocalValidatorFactoryBean();
}
This allows any beans, including ApplicationContext
, to be injected directly into a ConstraintValidator
:
@Constraint(validatedBy = DemoValidator.class)
public @interface DemoAnnotation {
// ...
Class<?> beanClass();
}
public class DemoValidator implements ConstraintValidator<DemoAnnotation, String> {
private final ApplicationContext applicationContext;
private Object bean;
@Autowired
public DemoValidator(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
@Override
public void initialize(DemoAnnotation constraint) {
Class<?> beanClass = constraint.beanClass();
bean = applicationContext.getBean(beanClass);
}
@Override
public boolean isValid(String obj, ConstraintValidatorContext context) {
return !obj.isEmpty();
}
}
Demo
For a really flexible validation solution I would recommend Jakub Jirutka's Bean Validator utilizing Spring Expression Language (SpEL) which allows things like:
public class Sample {
@SpELAssert("@myService.calculate(#this) > 42")
private int value;
}
来源:https://stackoverflow.com/questions/58088188/injecting-spring-dependencies-into-constrantvalidator