如何在Spring @Value批注中手动评估表达式?

时间:2018-09-22 00:36:18

标签: spring

我的SpringBoot应用程序具有一堆@Value注释。将应用程序部署到我们的Kubernetes集群时,最终使用的是通过两个不同机制插入的属性文件。当它最终到达那里时,如果开发人员犯了简单的错误,则容器可能无法启动,这仅仅是因为他们没有正确设置所有属性。直到犯错之后,才容易发现这是发生的事情。

请注意,实际上所有这些@Value批注将使用“ $ {}”语法,而不是“#{}”。主要关注的是从属性文件中读取特定属性,而不是从Spring bean属性中读取。

所以,我想写的是一个小的验证脚本(一个小的Java类),它执行以下操作:

  1. 获取生成的属性文件的路径
  2. 将该属性文件加载到Properties对象中
  3. 扫描所有类(带有基本包)的类路径以及这些类中的所有字段,以获取@Value注释
  4. 对于每个发现的@Value批注,进行一些简单的验证并评估表达式
  5. 如果验证或评估失败,请打印一条错误消息,其中包含所有相关详细信息。

此脚本将在“ kubectl推广”发生之前运行。如果在推出之前看到这些错误消息,我们将节省诊断这些问题的时间。

到目前为止,我已经能够完成所有工作,除了对已加载的属性文件进行某些操作并评估表达式。我知道Spring使用的是bean后处理器,但是我不知道如何手动调用它。

有什么主意如何实现缺失的链接吗?

更新

我仍然没有答案。

我当时想也许答案会在Spring代码库的BeanPostProcessor中找到,所以我克隆了spring-framework存储库。我发现了几个潜在的可能,分别是“ AutowiredAnnotationBeanPostProcessor”,“ BeanFactoryPostProcessor”,“ CommonAnnotationBeanPostProcessor”和“ BeanPostProcessor”,但是在这些值中我什么都没看到,就像在Value注释中评估表达式一样。我本来可以在注释的“ value()”方法中设置断点,但是当然不能在这样的方法中设置断点。

更新

要清楚,该表达式不是“ Spring EL”表达式。这些引用bean属性(或已加载的属性)并以“#{”开头。我正在使用仅引用以“ $ {”开头的属性的表达式。我确实尝试使用Spring EL解析该表达式,但它只是认为那里什么也没有。

1 个答案:

答案 0 :(得分:0)

我设法弄清楚了。关键是“ PropertyPlaceholderHelper.replacePlaceholders(String, Properties)”方法。使用它,我开发出了类似这样的东西:

        PropertyPlaceholderHelper   propertyPlaceholderHelper   =
                new PropertyPlaceholderHelper("${", "}", ":", true);

        ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(true);

        boolean foundAtLeastOneUnfoundProperty  = false;

        for (BeanDefinition bd : scanner.findCandidateComponents(basePackage)) {
            String beanClassName = bd.getBeanClassName();
            Class<?>    clazz   = Class.forName(beanClassName);
            for (Field field : clazz.getDeclaredFields()) {
                Value valueAnnotation = field.getAnnotation(Value.class);
                if (valueAnnotation != null) {
                    Matcher matcher = propertyRefPattern.matcher(valueAnnotation.value());
                    if (matcher.matches()) {
                        String  resultingValue  = propertyPlaceholderHelper.replacePlaceholders(valueAnnotation.value(), properties);
                        if (resultingValue.equals(valueAnnotation.value())) {
                            // This means that the property was not found.
                            System.out.println("ERROR: Expression \"" + valueAnnotation.value() +
                                               "\" on field \"" + field.getName() + "\" in class \"" + beanClassName +
                                               "\" references a property which is not defined.");
                            foundAtLeastOneUnfoundProperty  = true;
                        }
                    }
                }
            }
        }