Скажем, у меня есть собственный ConstraintValidator:
public class FooValidator implements ConstraintValidator<ValidFoo, String> {
@Override
public void initialize(final ValidFoo foo) {
// No-op
}
@Override
public boolean isValid(final String foo, final ConstraintValidatorContext context) {
}
}
Я хотел бы иметь возможность инициализировать этот класс, передав некоторую конфигурацию из ServiceConfiguration
в Dropwizard run
или initialize
.
Это возможно?
Во-первых, стоит отметить, что в предстоящем выпуске Dropwizard 2.0.0 есть встроенный в поддержку этого
На данный момент процесс немного сложен. Вы в основном хотите повторно запустить проверку Hibernate, но с настраиваемой фабрикой валидатора ограничений, которая будет поддерживать инъекцию.
В нем будет задействовано около 4 пользовательских классов, так что терпите меня. Поехали:
Во-первых, мы начинаем с регистрации настраиваемой функции для обертывания этой функции в нашем классе Application
:
public void run(MainConfiguration config, Environment environment) throws Exception {
// ...
environment.jersey().register(InjectingValidationFeature.class);
}
Теперь мы определяем функцию: InjectingValidationFeature
- она в основном регистрирует наши пользовательские реализации в сервисном контейнере:
public class InjectingValidationFeature implements Feature {
@Override
public boolean configure(FeatureContext context) {
context.register(new AbstractBinder() {
@Override
protected void configure() {
bindFactory(ValidatorFactory.class).to(Validator.class).in(Singleton.class);
bind(InjectingConfiguredValidator.class).to(ConfiguredValidator.class).in(Singleton.class);
bind(InjectingConstraintValidatorFactory.class).to(ConstraintValidatorFactory.class).in(Singleton.class);
}
});
return true;
}
}
Теперь мы определяем те классы, которые мы регистрируем выше. Начнем с основной части, InjectingConstraintValidatorFactory
, которую Hibernate Validator фактически будет использовать для создания валидаторов ограничений. Обратите внимание, что, поскольку мы регистрируем их в контейнере, мы уже можем начать вводить данные, вот наш собственный ConstraintValidatorFactory
, использующий локатор служб, чтобы сделать возможной инъекцию зависимостей:
public class InjectingConstraintValidatorFactory implements ConstraintValidatorFactory {
private final ServiceLocator serviceLocator;
@Inject
public InjectingConstraintValidatorFactory(ServiceLocator serviceLocator) {
this.serviceLocator = serviceLocator;
}
@Override
public <T extends ConstraintValidator<?, ?>> T getInstance(Class<T> key) {
return this.serviceLocator.createAndInitialize(key);
}
@Override
public void releaseInstance(ConstraintValidator<?, ?> instance) {
this.serviceLocator.preDestroy(instance);
}
}
Теперь наша фабрика для центрального интерфейса javax.validation.Validator
:
public class ValidatorFactory implements Factory<Validator> {
private final ConstraintValidatorFactory constraintValidatorFactory;
@Inject
public ValidatorFactory(ConstraintValidatorFactory constraintValidatorFactory) {
this.constraintValidatorFactory = constraintValidatorFactory;
}
@Override
public Validator provide() {
return Validation.byDefaultProvider().configure().constraintValidatorFactory(
this.constraintValidatorFactory).buildValidatorFactory()
.getValidator();
}
@Override
public void dispose(Validator instance) {
// Nothing
}
}
И, наконец, наш InjectingConfiguredValidator
, обратите внимание, как он просто использует DropwizardConfiguredValidator
, но с @Inject
, который позволит нам получать валидатор от нашего ValidatorFactory
, приведенного выше:
public class InjectingConfiguredValidator extends DropwizardConfiguredValidator {
@Inject
public InjectingConfiguredValidator(Validator validator) {
super(validator);
}
}
Вот и все. Благодаря вышесказанному нам удалось зарегистрировать Validator
с поддержкой инъекций в Jersey, а также в нашем сервисном контейнере, так что вы также можете @Inject Validator
где угодно и использовать его, как вам нравится.