首页 > 解决方案 > 无法从 BeanPostProcessor 中的占位符获取值

问题描述

我正在将 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);
    }
}

但是当我尝试实现这样BeanPostProcessor装饰器模式

@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);
    }
}

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

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

@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而不是使用问题。

标签: javaspring

解决方案


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

根据Spring Docs on PropertyPlaceholderConfigurer

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

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

更新的装饰类:

@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;
    }
}

应用程序属性

spring.kafka.maximumRequestSize=15728640

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

...
.....
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

推荐阅读