【问题标题】:Unable to get values from placeholders in BeanPostProcessor无法从 BeanPostProcessor 中的占位符获取值
【发布时间】:2021-02-22 11:51:22
【问题描述】:

我正在将 Spring 与 Spring Boot BOM 2.4.0 附带的遗留 Tomcat 应用程序(不是 Spring Boot)一起使用,问题类似于 Spring Expression Language not working,但我有一个具体案例。

如果我有一个 @Value("${spring.kafka.maximumRequestSize:15728640}") 和以下的依赖类

@Configuration
@Order
public class KafkaTracingDecorator implements  ApplicationContextAware {
    private KafkaTracing kafkaTracing;

    @Override
    public void setApplicationContext(final ApplicationContext applicationContext) throws BeansException {

        kafkaTracing = applicationContext.getBean(KafkaTracing.class);
    }
}

它有效。以下也有效,但我没有在后处理器上做任何事情

@Configuration
@Order
public class KafkaTracingDecorator implements BeanPostProcessor, ApplicationContextAware {

    @Override
    public Object postProcessAfterInitialization(final Object bean, final String beanName) throws BeansException {

        return bean;
    }

    @Override
    public void setApplicationContext(final ApplicationContext applicationContext) throws BeansException {

//        kafkaTracing = applicationContext.getBean(KafkaTracing.class);
    }
}

但是当我尝试像这样实现BeanPostProcessorimplement the decorator pattern

@Configuration
@Order
public class KafkaTracingDecorator implements BeanPostProcessor, ApplicationContextAware {

    private KafkaTracing kafkaTracing;

    @Override
    public Object postProcessBeforeInitialization(final Object bean, final String beanName) throws BeansException {

        if (bean instanceof KafkaProducer) {
            return kafkaTracing.producer((KafkaProducer) bean);
        } else if (bean instanceof KafkaConsumer) {
            return kafkaTracing.consumer((KafkaConsumer) bean);
        } else {
            return bean;
        }
    }

    @Override
    public void setApplicationContext(final ApplicationContext applicationContext) throws BeansException {

        kafkaTracing = applicationContext.getBean(KafkaTracing.class);
    }
}

在创建 WebApplicationContext 时我得到Caused by: java.lang.NumberFormatException: For input string: "${spring.kafka.maximumRequestSize:15728640}"

即使我不使用该值也会发生这种情况,因此它不会进入方法

@Configuration
@Order
public class KafkaTracingDecorator implements BeanPostProcessor, ApplicationContextAware {

    private KafkaTracing kafkaTracing;

    @Override
    public Object postProcessBeforeInitialization(final Object bean, final String beanName) throws BeansException {

        return bean;
    }

    @Override
    public void setApplicationContext(final ApplicationContext applicationContext) throws BeansException {

        kafkaTracing = applicationContext.getBean(KafkaTracing.class);
    }
}

这是一个非常简化的示例,但也以同样的方式失败:

@Configuration
public class KafkaTracingDecorator implements BeanPostProcessor {

    @Value("${spring.kafka.maximumRequestSize:15728640}")
    private int maxRequestSize;

}

(请注意,这在 Spring Boot 上按预期工作,因此可能与 Spring 的非引导使用有关)

导致错误的部分堆栈跟踪未列出任何自定义代码:

    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:660)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:640)
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:119)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:399)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1413)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:601)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:524)
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:213)
    at org.springframework.context.support.PostProcessorRegistrationDelegate.registerBeanPostProcessors(PostProcessorRegistrationDelegate.java:270)
    at org.springframework.context.support.AbstractApplicationContext.registerBeanPostProcessors(AbstractApplicationContext.java:761)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:566)
    at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:401)
    at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:292)
    at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:103)
    at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4689)

MVCE 添加:https://github.com/trajano/spring-beanpostproc-mvce 链接到 GitHub 问题,以防这是一个错误 https://github.com/spring-projects/spring-framework/issues/26571,而不是使用问题。

【问题讨论】:

  • 在构建 WebApplicationContext 时启动。更新了 Q 以包含该事实
  • 不能这样做,它不是一个开源项目,我正在尝试创建一个显示它的 MVCE,但它在 MVCE 本身中工作。
  • 谢谢,我基本上是在将 Spring Boot 启动器调整为 Tomcat + WAR 部署,希望问题会显现出来。
  • 我注意到它已经在 Spring Boot 上运行了。我确实添加了现在证明失败的 MVCE。在 Dockerized 的 tomcat 上。
  • @AnishB。你有答案吗?

标签: java spring


【解决方案1】:

您需要通过注册PropertySourcesPlaceholderConfigurer bean 来更新Decorator 类来解决您的问题。

根据Spring Docs on PropertyPlaceholderConfigurer

PropertyPlaceholderConfigurerPlaceholderConfigurerSupport 的子类。此子类有助于针对本地属性和/或系统属性和环境变量解析 ${...} 占位符。

因此,配置此 bean 将从类路径中加载 application.properties,它将能够解析 @Value 注释中使用的 ${..} 以获取实际值集。

更新了 Decorator 类:

@Configuration
public class Decorator implements BeanPostProcessor {
    
    @Value("${spring.kafka.maximumRequestSize}")
    private int maxRequestSize;
    
    @Bean
    public static PlaceholderConfigurerSupport placeholderConfigurerSupport() {
        PlaceholderConfigurerSupport support = new PropertySourcesPlaceholderConfigurer();
        support.setLocations(new ClassPathResource("application.properties"));
        return support;
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println(maxRequestSize);
        return bean;
    }
}

application.properties

spring.kafka.maximumRequestSize=15728640

Tomcat 8.5.57AdoptedOpenJDK 11.0.6 上运行的控制台日志:

...
.....
INFO: Initializing Spring root WebApplicationContext
Feb 20, 2021 9:21:41 AM org.springframework.web.context.ContextLoader initWebApplicationContext
INFO: Root WebApplicationContext: initialization started
15728640
Feb 20, 2021 9:21:41 AM org.springframework.web.context.ContextLoader initWebApplicationContext
INFO: Root WebApplicationContext initialized in 518 ms
Feb 20, 2021 9:21:41 AM org.apache.coyote.AbstractProtocol start
INFO: Starting ProtocolHandler ["http-nio-8080"]
Feb 20, 2021 9:21:41 AM org.apache.catalina.startup.Catalina start
INFO: Server startup in 1419 ms

【讨论】:

  • 是的,MVCE 与此更改一起工作,将在更大的项目中进行尝试。
  • 我正在考虑将此作为另一个问题提出,但您可以使用哪个资源让“环境变量”设置属性..
  • 此问题的第二部分,是否需要在此配置中定义支持 bean 还是可以在外部?我刚刚测试过,它可以在外面,将它分开而不是嵌入到Decorator中可能是一个更好的答案
  • 我还注意到你使用静态,我在 MVCE 上测试过,它看起来不需要静态
  • @ArchimedesTrajano 是的!但是如果没有静态,数据就不会被拾取。就我而言,它变为零了。在您的案例中,它是否从属性中获得了正确的价值?
猜你喜欢
  • 2020-10-19
  • 1970-01-01
  • 2017-07-27
  • 1970-01-01
  • 2013-09-16
  • 1970-01-01
  • 2012-10-03
  • 2014-04-26
  • 1970-01-01
相关资源
最近更新 更多