Spring 引导 JSR-303/349 配置
Spring Boot JSR-303/349 configuration
在我的 Spring Boot 1.5.1
应用程序中,我正在尝试配置对 JSR-303 / JSR-349 验证的支持。
我在我的方法中添加了以下注释@NotNull @Size(min = 1)
:
@Service
@Transactional
public class DecisionDaoImpl extends BaseDao implements DecisionDao {
@Override
public Decision create(@NotNull @Size(min = 1) String name, String description, String url, String imageUrl, Decision parentDecision, Tenant tenant, User user) {
...
}
}
我正在尝试从我的测试中调用此方法,但它不会因验证约束而失败。
这是我的测试和配置:
@SpringBootTest(classes = { TestConfig.class, Neo4jTestConfig.class })
@RunWith(SpringRunner.class)
@Transactional
public class TenantTest {
@Test
public void testCreateDecision() {
User user1 = userService.createUser("test1", "test1", "test1@test.com", null, null);
Tenant tenant1 = tenantDao.create("Tenant 1", "Tenant 1 description", false, user1);
// the following line should fail on the validation constraint because name parameter is null but it doesn't
final Decision rootDecision = decisionDao.create(null, "Root decision 1 description", null, tenant1, user1);
...
@Configuration
@ComponentScan("com.example")
@SpringBootApplication(exclude={Neo4jDataAutoConfiguration.class})
public class TestConfig {
}
我做错了什么以及如何在那里配置 JSR-303?
已更新
我已经添加了
public Decision create(@Valid @NotNull @Size(min = 1) String name, String description, Decision parentDecision, Tenant tenant, User author) {
但还是不行
我已将 @Validated
添加到我的 DecisionDaoImpl
但它现在失败了,出现以下异常:
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'decisionDaoImpl': Bean with name 'decisionDaoImpl' has been injected into other beans [criterionGroupDaoImpl,characteristicGroupDaoImpl,tenantDaoImpl] in its raw version as part of a circular reference, but has eventually been wrapped. This means that said other beans do not use the final version of the bean. This is often the result of over-eager type matching - consider using 'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:585)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483)
at org.springframework.beans.factory.support.AbstractBeanFactory.getObject(AbstractBeanFactory.java:306)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:208)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1138)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1066)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:585)
... 43 common frames omitted
我还在自动装配 DecisionDao
的地方添加了 @Lazy
注释,但现在我的测试失败了,出现以下异常:
javax.validation.ConstraintDeclarationException: HV000151: A method overriding another method must not alter the parameter constraint configuration, but method public com.example.domain.model.entity.decision.Decision com.example.domain.dao.decision.DecisionDaoImpl.create(java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.Long,java.lang.Long,com.example.domain.model.entity.user.User) changes the configuration of public abstract com.example.domain.model.entity.decision.Decision com.example.domain.dao.decision.DecisionDao.create(java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.Long,java.lang.Long,com.example.domain.model.entity.user.User).
at org.hibernate.validator.internal.metadata.aggregated.rule.OverridingMethodMustNotAlterParameterConstraints.apply(OverridingMethodMustNotAlterParameterConstraints.java:24)
at org.hibernate.validator.internal.metadata.aggregated.ExecutableMetaData$Builder.assertCorrectnessOfConfiguration(ExecutableMetaData.java:456)
您需要 @Valid
注释
标记一个 属性、方法参数或方法 return 类型以进行验证级联。
在对象上定义的约束及其属性在
属性,方法参数或方法 return 类型已验证。
将验证移至界面,如下:
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
public interface DecisionDao {
Decision create(@Valid @NotNull @Size(min = 1) String name,
String description, String url, String imageUrl);
}
将你的DecisionDaoImpl
注释为@Validated
,如下:
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
@Service
@Validated
public class DecisionDaoImpl extends BaseDao implements DecisionDao {
@Override
public Decision create(String name,
String description, String url, String imageUrl) {
System.out.println(name);
return new Decision();
}
}
修改您的测试用例以使用 assertj or ExpectedException 验证 javax.validation.ConstraintViolationException
,如下所示:
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import javax.validation.ConstraintViolationException;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;
@ContextConfiguration(classes = { TenantTest.Config.class })
@RunWith(SpringRunner.class)
public class TenantTest {
@Autowired
private DecisionDao decisionDao;
@Rule
public ExpectedException expectedException = ExpectedException.none();
@Test
public void testCreateDecisionUsingAssertj() {
assertThatExceptionOfType(ConstraintViolationException.class)
.isThrownBy(
() -> decisionDao.create(null,
"Root decision 1 description", null, null));
}
@Test
public void testCreateDecision() {
expectedException.expect(ConstraintViolationException.class);
decisionDao.create(null, "Root decision 1 description", null, null);
}
@Configuration
public static class Config {
@Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
return new MethodValidationPostProcessor();
}
@Bean
public DecisionDao decisionDao() {
return new DecisionDaoImpl();
}
}
}
确保你的类路径中有 hibernate-validator 以及@StanislavL 回答:
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
</dependency>
以及 org.assertj.core.api.Assertions.assertThatExceptionOfType
的可选依赖项,如:
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>3.3.0</version>
<scope>test</scope>
</dependency>
示例可以参考arpitaggarwal/jsr-303
约束注释旨在应用于 JavaBean。参见 http://beanvalidation.org/1.0/spec/#constraintsdefinitionimplementation-constraintdefinition
您在 DAO 中应用了约束注释 @NotNull
、@Size
等。您必须创建一个 Java Bean,例如"Person",它包装了那些属性(名称、描述等),然后将 "Person" 作为参数传递给 Controller 方法。如果您需要使用 DAO 而不是控制器,则需要对其进行检测以执行验证。在 AOP 等方面,您可能只能靠自己了,除非自此 post 之后发生了一些变化:http://forum.spring.io/forum/spring-projects/container/82643-annotation-driven-jsr-303-validation-on-service-and-dao-tier
更新:好吧,看起来现在支持(方法级别验证 JSR-349)请参阅 http://blog.codeleak.pl/2012/03/how-to-method-level-validation-in.html 的示例,类似于 Arpit 的回答。
更新了问题标题以反映最新的 JSR。
在我的 Spring Boot 1.5.1
应用程序中,我正在尝试配置对 JSR-303 / JSR-349 验证的支持。
我在我的方法中添加了以下注释@NotNull @Size(min = 1)
:
@Service
@Transactional
public class DecisionDaoImpl extends BaseDao implements DecisionDao {
@Override
public Decision create(@NotNull @Size(min = 1) String name, String description, String url, String imageUrl, Decision parentDecision, Tenant tenant, User user) {
...
}
}
我正在尝试从我的测试中调用此方法,但它不会因验证约束而失败。
这是我的测试和配置:
@SpringBootTest(classes = { TestConfig.class, Neo4jTestConfig.class })
@RunWith(SpringRunner.class)
@Transactional
public class TenantTest {
@Test
public void testCreateDecision() {
User user1 = userService.createUser("test1", "test1", "test1@test.com", null, null);
Tenant tenant1 = tenantDao.create("Tenant 1", "Tenant 1 description", false, user1);
// the following line should fail on the validation constraint because name parameter is null but it doesn't
final Decision rootDecision = decisionDao.create(null, "Root decision 1 description", null, tenant1, user1);
...
@Configuration
@ComponentScan("com.example")
@SpringBootApplication(exclude={Neo4jDataAutoConfiguration.class})
public class TestConfig {
}
我做错了什么以及如何在那里配置 JSR-303?
已更新
我已经添加了
public Decision create(@Valid @NotNull @Size(min = 1) String name, String description, Decision parentDecision, Tenant tenant, User author) {
但还是不行
我已将 @Validated
添加到我的 DecisionDaoImpl
但它现在失败了,出现以下异常:
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'decisionDaoImpl': Bean with name 'decisionDaoImpl' has been injected into other beans [criterionGroupDaoImpl,characteristicGroupDaoImpl,tenantDaoImpl] in its raw version as part of a circular reference, but has eventually been wrapped. This means that said other beans do not use the final version of the bean. This is often the result of over-eager type matching - consider using 'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:585)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483)
at org.springframework.beans.factory.support.AbstractBeanFactory.getObject(AbstractBeanFactory.java:306)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:208)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1138)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1066)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:585)
... 43 common frames omitted
我还在自动装配 DecisionDao
的地方添加了 @Lazy
注释,但现在我的测试失败了,出现以下异常:
javax.validation.ConstraintDeclarationException: HV000151: A method overriding another method must not alter the parameter constraint configuration, but method public com.example.domain.model.entity.decision.Decision com.example.domain.dao.decision.DecisionDaoImpl.create(java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.Long,java.lang.Long,com.example.domain.model.entity.user.User) changes the configuration of public abstract com.example.domain.model.entity.decision.Decision com.example.domain.dao.decision.DecisionDao.create(java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.Long,java.lang.Long,com.example.domain.model.entity.user.User).
at org.hibernate.validator.internal.metadata.aggregated.rule.OverridingMethodMustNotAlterParameterConstraints.apply(OverridingMethodMustNotAlterParameterConstraints.java:24)
at org.hibernate.validator.internal.metadata.aggregated.ExecutableMetaData$Builder.assertCorrectnessOfConfiguration(ExecutableMetaData.java:456)
您需要 @Valid
注释
标记一个 属性、方法参数或方法 return 类型以进行验证级联。 在对象上定义的约束及其属性在 属性,方法参数或方法 return 类型已验证。
将验证移至界面,如下:
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
public interface DecisionDao {
Decision create(@Valid @NotNull @Size(min = 1) String name,
String description, String url, String imageUrl);
}
将你的DecisionDaoImpl
注释为@Validated
,如下:
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
@Service
@Validated
public class DecisionDaoImpl extends BaseDao implements DecisionDao {
@Override
public Decision create(String name,
String description, String url, String imageUrl) {
System.out.println(name);
return new Decision();
}
}
修改您的测试用例以使用 assertj or ExpectedException 验证 javax.validation.ConstraintViolationException
,如下所示:
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import javax.validation.ConstraintViolationException;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;
@ContextConfiguration(classes = { TenantTest.Config.class })
@RunWith(SpringRunner.class)
public class TenantTest {
@Autowired
private DecisionDao decisionDao;
@Rule
public ExpectedException expectedException = ExpectedException.none();
@Test
public void testCreateDecisionUsingAssertj() {
assertThatExceptionOfType(ConstraintViolationException.class)
.isThrownBy(
() -> decisionDao.create(null,
"Root decision 1 description", null, null));
}
@Test
public void testCreateDecision() {
expectedException.expect(ConstraintViolationException.class);
decisionDao.create(null, "Root decision 1 description", null, null);
}
@Configuration
public static class Config {
@Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
return new MethodValidationPostProcessor();
}
@Bean
public DecisionDao decisionDao() {
return new DecisionDaoImpl();
}
}
}
确保你的类路径中有 hibernate-validator 以及@StanislavL 回答:
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
</dependency>
以及 org.assertj.core.api.Assertions.assertThatExceptionOfType
的可选依赖项,如:
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>3.3.0</version>
<scope>test</scope>
</dependency>
示例可以参考arpitaggarwal/jsr-303
约束注释旨在应用于 JavaBean。参见 http://beanvalidation.org/1.0/spec/#constraintsdefinitionimplementation-constraintdefinition
您在 DAO 中应用了约束注释 @NotNull
、@Size
等。您必须创建一个 Java Bean,例如"Person",它包装了那些属性(名称、描述等),然后将 "Person" 作为参数传递给 Controller 方法。如果您需要使用 DAO 而不是控制器,则需要对其进行检测以执行验证。在 AOP 等方面,您可能只能靠自己了,除非自此 post 之后发生了一些变化:http://forum.spring.io/forum/spring-projects/container/82643-annotation-driven-jsr-303-validation-on-service-and-dao-tier
更新:好吧,看起来现在支持(方法级别验证 JSR-349)请参阅 http://blog.codeleak.pl/2012/03/how-to-method-level-validation-in.html 的示例,类似于 Arpit 的回答。 更新了问题标题以反映最新的 JSR。