6

我正在尝试构建一个 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();
    }
}
4

1 回答 1

5

回答您的更新 2:这不适用于您的原始配置(使用 注册 PropertySource @PostConstruct),因为 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);
    }
}
于 2012-08-12T22:10:11.377 回答