我可以在没有Spring AppContext的情况下手动加载@ConfigurationProperties吗?

2022-09-03 04:35:59

有没有办法加载一个标有的类而不直接使用Spring Context?基本上,我想重用Spring所做的所有智能逻辑,但对于Bean,我在Spring生命周期之外手动实例化。@ConfigurationProperties

我有一个在Spring(Boot)中愉快加载的bean,我可以将其注入到我的其他服务bean中:

@ConfigurationProperties(prefix="my")
public class MySettings {
    String property1;
    File property2;
} 

请参阅春季文档,了解更多信息 http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#boot-features-external-config-command-line-args

但是现在我需要从Spring之外(由Hibernate创建的类)访问这个bean。该类是在应用启动过程的早期创建的,以至于 Spring Boot 尚未通过经典的查找帮助程序方法或滚动我自己的静态引用来提供应用程序上下文。

所以我想做这样的事情:

MySettings mySettings = new MySettings(); 
SpringPropertyLoadingMagicClass loader = new SpringPropertyLoadingMagicClass();
loader.populatePropertyValues(mySettings);

并让 MySettings 最终从命令行、系统属性、app.properties 等加载其所有值。Spring中是否有某个类可以执行此类操作,或者它是否与应用程序上下文交织在一起?

显然,我可以自己加载属性文件,但我真的想保留Spring Boot的逻辑,使用命令行变量(例如--my.property1=xxx),或系统变量,或appplicat.properties甚至yaml文件,以及它围绕宽松绑定和类型转换的逻辑(例如,property2是一个文件),以便它的工作方式与在Spring上下文中使用时完全相同。

可能还是白日梦?

感谢您的帮助!


答案 1

我有同样的“问题”。以下是我在SpringBoot版本 1.3.xxx 和1.4.1中如何解决它。

假设我们有以下 yaml 配置文件:

foo:
  apis:
      -
       name: Happy Api
       path: /happyApi.json?v=bar
      -
       name: Grumpy Api
       path: /grumpyApi.json?v=grrr

我们有以下:ConfigurationProperties

@ConfigurationProperties(prefix = "foo")
public class ApisProperties {
    private List<ApiPath> apis = Lists.newArrayList();

    public ApisProperties() {
    }

    public List<ApiPath> getApis() {
        return apis;
    }

    public static class ApiPath {
        private String name;
        private String path;

        public String getName() {
            return name;
        }

        public void setName(final String aName) {
            name = aName;
        }

        public String getPath() {
            return path;
        }

        public void setPath(final String aPath) {
            path = aPath;
        }
    }
} 

然后,要以编程方式执行Spring Boot的“神奇”操作(例如,在静态方法中加载某些属性),您可以执行以下操作:

private static ApisProperties apiProperties() {
    try {
        ClassPathResource resource;
        resource = new ClassPathResource("/config/application.yml");

        YamlPropertiesFactoryBean factoryBean;
        factoryBean = new YamlPropertiesFactoryBean();
        factoryBean.setSingleton(true); // optional depends on your use-case
        factoryBean.setResources(resource);

        Properties properties;
        properties = factoryBean.getObject();

        MutablePropertySources propertySources;
        propertySources = new MutablePropertySources();
        propertySources.addLast(new PropertiesPropertySource("apis", properties));

        ApisProperties apisProperties;
        apisProperties = new ApisProperties();

        PropertiesConfigurationFactory<ApisProperties> configurationFactory;
        configurationFactory = new PropertiesConfigurationFactory<>(apisProperties);
        configurationFactory.setPropertySources(propertySources);
        configurationFactory.setTargetName("foo"); // it's the same prefix as the one defined in the @ConfigurationProperties

        configurationFactory.bindPropertiesToTarget();
        return apisProperties; // apiProperties are fed with the values defined in the application.yaml

    } catch (BindException e) {
        throw new IllegalArgumentException(e);

    }
}

答案 2

以下是ctranxuan对Spring Boot 2.x的答案的更新。在我们的情况下,我们避免为单元测试启动Spring上下文,但喜欢测试我们的配置类(在此示例中调用,其设置以):AppConfigapp

public class AppConfigTest {
  private static AppConfig config;

  @BeforeClass
  public static void init() {
    YamlPropertiesFactoryBean factoryBean = new YamlPropertiesFactoryBean();
    factoryBean.setResources(new ClassPathResource("application.yaml"));

    Properties properties = factoryBean.getObject();

    ConfigurationPropertySource propertySource = new MapConfigurationPropertySource(properties);
    Binder binder = new Binder(propertySource);

    config = binder.bind("app", AppConfig.class).get(); // same prefix as @ConfigurationProperties
  }
}

推荐