web-dev-qa-db-ja.com

TypesafeConfigに裏打ちされたSpring環境

プロジェクトでtypesafeconfig(HOCON構成ファイル)を使用したいのですが、これにより、簡単で整理されたアプリケーション構成が容易になります。現在、私は通常のJavaプロパティファイル(application.properties)を使用していますが、これは大きなプロジェクトでは処理が困難です。

私のプロジェクトはSpringMVCです(Spring Bootプロジェクトではありません)。 (サービスに注入されている)Spring環境をバックアップしてtypesafeconfigでバックアップする方法はありますか? @Valueアノテーション、@Autowired Environmentなど、既存の環境の使用に支障をきたすことはありません。

最小限の労力とコードの変更でこれを行うにはどうすればよいですか。

これは私の現在の解決策です:他にもっと良い方法はありますか

@Configuration
public class PropertyLoader{
    private static Logger logger = LoggerFactory.getLogger(PropertyLoader.class);

    @Bean
    @Autowired
    public static PropertySourcesPlaceholderConfigurer properties(Environment env) {
        PropertySourcesPlaceholderConfigurer pspc = new PropertySourcesPlaceholderConfigurer();

        Config conf = ConfigFactory.load();
        conf.resolve();
        TypesafePropertySource propertySource = new TypesafePropertySource("hoconSource", conf);

        ConfigurableEnvironment environment = (StandardEnvironment)env;
        MutablePropertySources propertySources = environment.getPropertySources();
        propertySources.addLast(propertySource);
        pspc.setPropertySources(propertySources);

        return pspc;
    }
}

class TypesafePropertySource extends PropertySource<Config>{
    public TypesafePropertySource(String name, Config source) {
        super(name, source);
    }

    @Override
    public Object getProperty(String name) {
        return this.getSource().getAnyRef(name);
    }
}
20
Ysak

プロパティソースにPropertySourceを手動で追加するよりも、少し慣用的な方法を思いついたと思います。 PropertySourceFactoryを作成し、それを@PropertySourceで参照します

まず、あなたが持っているものとほぼ同じTypesafeConfigPropertySourceがあります。

public class TypesafeConfigPropertySource extends PropertySource<Config> {
    public TypesafeConfigPropertySource(String name, Config source) {
        super(name, source);
    }

    @Override
    public Object getProperty(String path) {
        if (source.hasPath(path)) {
            return source.getAnyRef(path);
        }
        return null;
    }
}

次に、そのプロパティソースを返すPropertySource factoryを作成します

public class TypesafePropertySourceFactory implements PropertySourceFactory {

    @Override
    public PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException {
        Config config = ConfigFactory.load(resource.getResource().getFilename()).resolve();

        String safeName = name == null ? "typeSafe" : name;
        return new TypesafeConfigPropertySource(safeName, config);
    }

}

そして最後に、構成ファイルでは、PropertySourceを自分で追加する代わりに、他のPropertySourceと同じようにプロパティソースを参照できます。

@Configuration
@PropertySource(factory=TypesafePropertySourceFactory.class, value="someconfig.conf")
public class PropertyLoader {
    // Nothing needed here
}
14
Laplie Anderson

次のようにPropertySourceクラスを作成します。これは、値またはnullを返す必要があり、libが欠落している例外をスローしないようにする必要があるという点が異なります。

public class TypesafeConfigPropertySource extends PropertySource<Config> {

    private static final Logger LOG = getLogger(TypesafeConfigPropertySource.class);

    public TypesafeConfigPropertySource(String name, Config source) {
        super(name, source);
    }

    @Override
    public Object getProperty(String name) {
        try {
            return source.getAnyRef(name);
        } catch (ConfigException.Missing missing) {
            LOG.trace("Property requested [{}] is not set", name);
            return null;
        }
    }
}

2番目のステップは、Beanを次のように定義することです。

    @Bean
    public TypesafeConfigPropertySource provideTypesafeConfigPropertySource(
        ConfigurableEnvironment env) {

        Config conf = ConfigFactory.load().resolve();
        TypesafeConfigPropertySource source = 
                          new TypesafeConfigPropertySource("typeSafe", conf);
        MutablePropertySources sources = env.getPropertySources();
        sources.addFirst(source); // Choose if you want it first or last
        return source;

    }

プロパティを他のBeanに自動配線する場合は、最初にロードされるように、propertysourceBeanにアノテーション@DependsOnを使用する必要があります。

それが役に立てば幸い

5
tbo

Laplie Andersonは、いくつかの小さな改善を加えて答えています。

  • リソースが見つからない場合は例外をスローします
  • [および:文字を含むパスを無視する

TypesafePropertySourceFactory.Java

import Java.io.IOException;

import org.springframework.core.env.PropertySource;
import org.springframework.core.io.support.EncodedResource;
import org.springframework.core.io.support.PropertySourceFactory;

import com.typesafe.config.Config;
import com.typesafe.config.ConfigFactory;
import com.typesafe.config.ConfigParseOptions;
import com.typesafe.config.ConfigResolveOptions;

public class TypesafePropertySourceFactory implements PropertySourceFactory {

  @Override
  public PropertySource<?> createPropertySource(String name, EncodedResource resource)
      throws IOException {
    Config config = ConfigFactory
        .load(resource.getResource().getFilename(),
            ConfigParseOptions.defaults().setAllowMissing(false),
            ConfigResolveOptions.noSystem()).resolve();

    String safeName = name == null ? "typeSafe" : name;
    return new TypesafeConfigPropertySource(safeName, config);
  }
}

TypesafeConfigPropertySource。Java

import org.springframework.core.env.PropertySource;

import com.typesafe.config.Config;

public class TypesafeConfigPropertySource extends PropertySource<Config> {
  public TypesafeConfigPropertySource(String name, Config source) {
    super(name, source);
  }

  @Override
  public Object getProperty(String path) {
    if (path.contains("["))
      return null;
    if (path.contains(":"))
      return null;
    if (source.hasPath(path)) {
      return source.getAnyRef(path);
    }
    return null;
  }
}
1
raisercostin

上記のすべてを試しましたが失敗しました。私が抱えていた特定の問題の1つは、Beanの初期化順序でした。たとえば、タイプセーフ構成から取得され、他のプロパティでも同じであるオーバーライドされたプロパティを取得するために、フライウェイのサポートが必要でした。

M-deinumからのコメントの1つで示唆されているように、他の回答からの入力にも依存して、次のソリューションが機能します。メインアプリをロードするときにApplicationContextInitializerを使用することで、アプリの開始時に小道具がロードされ、「env」に正しくマージされていることを確認します。

import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.annotation.Import;

@SpringBootConfiguration
@Import({MyAppConfiguration.class})
public class MyApp {

    public static void main(String[] args) {
        new SpringApplicationBuilder(MyApp.class)
            .initializers(new MyAppContextInitializer())
            .run(args);
    }
}

ContextInitializerは次のようになります。

import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;

public class MyAppContextInitializer implements
    ApplicationContextInitializer<ConfigurableApplicationContext> {

    @Override
    public void initialize(ConfigurableApplicationContext ac) {    
        PropertiesLoader loader = new PropertiesLoader(ac.getEnvironment());
        loader.addConfigToEnv();
    }

} 

PropertiesLoaderは次のように機能して、構成からプロパティをロードし、環境に詰め込みます。

import com.typesafe.config.Config;
import com.typesafe.config.ConfigFactory;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MutablePropertySources;

class PropertiesLoader {

    private ConfigurableEnvironment env;

    public PropertiesLoader(ConfigurableEnvironment env) {
        this.env = env;
    }

    public void addConfigToEnv() {
        MutablePropertySources sources = env.getPropertySources();

        Config finalConfig = ConfigFactory.load().resolve();
        // you can also do other stuff like: ConfigFactory.parseFile(), use Config.withFallback to merge configs, etc.
        TypesafeConfigPropertySource source = new TypesafeConfigPropertySource("typeSafe", finalConfig);

        sources.addFirst(source);
    }

}

また、typesafeconfigで機能するTypesafeConfigPropertySourceも必要です。

import com.typesafe.config.Config;
import org.springframework.core.env.PropertySource;

public class TypesafeConfigPropertySource extends PropertySource<Config> {

    public TypesafeConfigPropertySource(String name, Config source) {
        super(name, source);
    }

    @Override
    public Object getProperty(String path) {
        if (path.contains("["))
            return null;
        if (path.contains(":"))
            return null;
        if (source.hasPath(path)) {
            return source.getAnyRef(path);
        }
        return null;
    }

}
0
Fabian