【问题标题】:Custom spring property source does not resolve placeholders in @Value自定义弹簧属性源不解析 @Value 中的占位符
【发布时间】:2012-08-09 04:32:43
【问题描述】:

我正在尝试构建一个 Spring 3.1 PropertySource,它从 Zookeeper 节点读取其值。为了连接到 Zookeeper,我使用了来自 Netflix 的 Curator

为此,我构建了一个自定义属性源,它从 Zookeeper 读取属性的值并返回它。当我像这样解析属性时,这很好用

ZookeeperPropertySource zkPropertySource = new ZookeeperPropertySource(zkClient);
ctx.getEnvironment().getPropertySources().addLast(zkPropertySource);
ctx.getEnvironment().getProperty("foo"); // returns 'from zookeeper'

但是,当我尝试实例化一个带有 @Value 注释的字段的 bean 时,这会失败:

@Component
public class MyBean {
    @Value("${foo}") public String foo;
}

MyBean b = ctx.getBean(MyBean.class); // fails with BeanCreationException

这个问题很可能与 Zookeeper 无关,而是与我注册属性源和创建 bean 的方式有关。

高度赞赏任何见解。

更新 1:

我正在从这样的 XML 文件创建应用上下文:

public class Main {
    public static void main(String[] args) throws Exception {
        ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        ctx.registerShutdownHook();
    }
}

连接到 Zookeeper 的类是 @Component。

@Component
public class Server {
    CuratorFramework zkClient;

    public void connectToZookeeper() {
        zkClient = ... (curator magic) ...
    }

    public void registerPropertySource() {
        ZookeeperPropertySource zkPropertySource = new ZookeeperPropertySource(zkClient);
        ctx.getEnvironment().getPropertySources().addLast(zkPropertySource);
        ctx.getEnvironment().getProperty("foo"); // returns 'from zookeeper'
    }

    @PostConstruct
    public void start() {
        connectToZookeeper();
        registerPropertySource();
        MyBean b = ctx.getBean(MyBean.class);
    }
}

更新 2

当我使用无 XML 配置时,这似乎有效,即 @Configuration、@ComponentScan 和 @PropertySource 与 AnnotationConfigApplicationContext 结合使用。为什么它不适用于 ClassPathXmlApplicationContext?

@Configuration
@ComponentScan("com.goleft")
@PropertySource({"classpath:config.properties","classpath:version.properties"})
public class AppConfig {
    @Bean
    public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
        return new PropertySourcesPlaceholderConfigurer();
    }
}

【问题讨论】:

    标签: spring properties apache-zookeeper


    【解决方案1】:

    回答您的更新 2: 这不适用于您的原始配置(使用 @PostConstruct 注册 PropertySource),因为 PropertySource 注册的时间很晚,此时您的目标 bean 已经被构造和初始化。

    占位符的注入通常通过 BeanFactoryPostProcessor 进行,这在 Spring 生命周期的早期(在此阶段尚未创建 bean),如果在该阶段注册了 PropertySource,则应解析占位符。

    最好的方法是使用 ApplicationContextInitializer,获取 applicationContext 的句柄并在那里注册 propertySource:

    public class CustomInitializer implements ApplicationContextInitializer<ConfigurableWebApplicationContext> {
        public void initialize(ConfigurableWebApplicationContext ctx) {
            ZookeeperPropertySource zkPropertySource = new ZookeeperPropertySource(zkClient);
            ctx.getEnvironment().getPropertySources().addFirst(zkPropertySource);
        }
    }
    

    【讨论】:

    • 我已经简化了问题,但现在我不能让它工作了。它总是从配置文件加载。好吧,实际上它在我的非简化项目中确实有效,但仅适用于 AppConfig 类并且该属性在 config.properties 文件中不存在时。如何将 ApplicationContextInitializer 与非 web 应用一起使用?
    • 是的,你是对的,它的工作方式有点不同。我已经向您的存储库发送了一个拉取请求,其中包含一个针对注释和基于 xml 的应用程序上下文的行为一致的测试。
    • 非常感谢,顺便说一句,这很有效并且代码很干净。问题是,对于 ClasspathXmlApplicationContext,您必须在 context:property-placeholder 中设置 ignore-unresolvable="false"。否则会失败。
    猜你喜欢
    • 2022-11-01
    • 1970-01-01
    • 2015-05-22
    • 1970-01-01
    • 1970-01-01
    • 2019-08-18
    • 1970-01-01
    • 1970-01-01
    • 2017-01-21
    相关资源
    最近更新 更多