如果${service.property}
不是空字符串,我该如何检查?如果是,则抛出某种可读的异常?它必须在Bean创建期间发生。
@Component
public class Service {
@Value("${service.property}")
private String property;
}
我正在寻找最简单的方式(编写最少的代码)。如果使用注释会很棒。
我目前的解决方案是执行"手写"在属性的setter中进行验证,但对于这样简单的事情来说,代码太多了。
提示:我找了一些使用SpEL的方法,因为我已经在@Value
内使用了它,但是到目前为止我发现它并不那么容易/干净。但本来可以忽视一些事情。
澄清:预期的行为是,应用程序不启动。目标是确保所有属性都已设置,尤其是字符串属性不为空。错误应该清楚地说,缺少什么。我不想设置任何默认值!用户必须全部设置。
答案 0 :(得分:2)
你有什么工作。如果您未在属性文件中包含该属性,则在服务器启动时会收到org.springframework.beans.factory.BeanCreationException
例外。
Apr 22, 2015 9:47:37 AM org.apache.catalina.core.ApplicationContext log
SEVERE: StandardWrapper.Throwable
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'service': Injection of resource dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'service': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private java.lang.String com.util.Service.property; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'service.property' in string value "${service.property}"
at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.postProcessPropertyValues(CommonAnnotationBeanPostProcessor.java:306)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1146)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:519)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:458)
替代方法是使用initProperty
来处理或设置值,这里是您可以抛出某种可读异常的地方。
@Component
public class Service {
private String property;
@Autowired
public void initProperty(@Value("${service.property}") String property) {
if(property == null) {
// Error handling here
}
}
}
这取决于您是否希望应用程序启动,无论属性是否已设置,如果没有,请向日志或控制台抛出可读的异常,然后使用默认值设置它,或者如果您希望抛出错误在服务器启动和bean创建。
我想第三个选项是设置值,如果没有使用默认的setter给出。
@Component
public class Service {
@Value("${service.property:'This is my default setter string'}")
private String property;
}
答案 1 :(得分:1)
您可以将该组件用作属性占位符本身。然后你可以使用你想要的任何验证。
@Component
@Validated
@PropertySource("classpath:my.properties")
@ConfigurationProperties(prefix = "my")
public class MyService {
@NotBlank
private String username;
public void setUsername(String username) {
this.username = username;
}
...
}
您的 my.properties 文件将如下所示:
my.username=felipe
答案 2 :(得分:0)
这是我的解决方案,只需将该类放入代码中(只需修复“my.package”字符串):
/**
* Validates the environment-dependent properties during application start. Finds all spring beans, which classes are in
* defined package, validates them and in case of error tries to log the property name (not class field name), taken
* from {@link Value} annotation.
*
* @author Tomasz
*/
@Component
public class ConfigurationChecker implements ApplicationListener<ContextRefreshedEvent> {
private static final Logger LOG = LoggerFactory.getLogger(ConfigurationChecker.class);
// this is a property, that is set in XML, so we bind it here to be found by checker. For properties wired directly in Beans using @Value just add validation constraints
@Value("${authorization.ldap.url}")
@NotBlank
private String ldapUrl;
private static final String FAIL_FAST_PROPERTY = "hibernate.validator.fail_fast";
private Validator validator = Validation.byDefaultProvider().configure().addProperty(FAIL_FAST_PROPERTY, "false")
.buildValidatorFactory().getValidator();
/**
* Performs the validation and writes all errors to the log.
*/
@SneakyThrows
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
LOG.info("Validating properties");
Set<ConstraintViolation<Object>> allViolations = new HashSet<>();
// Find all spring managed beans (including ConfigurationChecker)...
for (String beanName : event.getApplicationContext().getBeanDefinitionNames()) {
Object bean = event.getApplicationContext().getBean(beanName);
// ...but validate only ours.
if (bean.getClass().getCanonicalName().startsWith("my.package")) {
Set<ConstraintViolation<Object>> viol = this.validator.validate(bean);
LOG.info("Bean '" + beanName + "': " + (viol.isEmpty() ? " OK" : viol.size() + " errors found"));
allViolations.addAll(viol);
} else {
continue;
}
}
// if any error found...
if (allViolations.size() > 0) {
for (ConstraintViolation<Object> violation : allViolations) {
// ...extract "property.name" from field annotation like @Value("${property.name}")
String propertyName = violation.getLeafBean().getClass()
.getDeclaredField(violation.getPropertyPath().toString()).getAnnotation(Value.class).value();
propertyName = StringUtils.substring(propertyName, 2, -1);
// .. log it ..
LOG.error(propertyName + " " + violation.getMessage());
}
// ... and do not let the app start up.
throw new IllegalArgumentException("Invalid configuration detected. Please check the log for details.");
}
}
}
这里是对它的测试:
@RunWith(EasyMockRunner.class)
public class ConfigurationCheckerTest extends EasyMockSupport {
@TestSubject
private ConfigurationChecker checker = new ConfigurationChecker();
@Mock
private ContextRefreshedEvent event;
@Mock
private ApplicationContext applicationContext;
@Test(expected = IllegalArgumentException.class)
public void test() {
expect(this.event.getApplicationContext()).andReturn(this.applicationContext).anyTimes();
expect(this.applicationContext.getBeanDefinitionNames()).andReturn(new String[] { "configurationChecker" });
expect(this.applicationContext.getBean("configurationChecker")).andReturn(this.checker);
replayAll();
this.checker.onApplicationEvent(this.event);
verifyAll();
}
}