Javaの構成ファイルから構成パラメーターを読み取る最良の方法は何ですか?
実行時まで、構成の詳細がわからないことを想定しましょう(アプリケーションを実行する前に、config
ファイルでこれらのパラメーターを構成する必要がある場合があります。
これらの構成の詳細を読み、アプリケーションで必要な場所で再利用する必要があります。そのために、それらをグローバル定数(public static final
)として作成します。
したがって、疑わしいのは、必要なクラスから直接config
ファイルから読み取る場合、パフォーマンスに影響があるのでしょうか?なぜなら、ランタイム値は個別のInterface
に直接入れることはできません。
パフォーマンスに影響を与えると考えています。これを行うより良い方法を提案してください。
PDATE:設定の詳細に個別の最終クラスを使用できますか?すべての設定の詳細を定数として別のpublic final class
に入れます
(構成ファイルからすべての構成詳細を一度に読み取り、後でアプリケーションで使用するためにグローバル定数として保存するため)
パフォーマンスに影響すると思います。
これが真実であるとは思わない。
アプリケーションが起動時に構成ファイルを1回だけ読み取ると仮定すると、ファイルの読み取りにかかる時間は、おそらくアプリケーションの全体的なパフォーマンスとは無関係です。実際、アプリケーションの実行時間が長くなればなるほど、起動時間はそれほど重要ではなくなります。
標準的なアドバイスは、パフォーマンスisが重要な問題であるという具体的な証拠(測定値)がある場合にのみ、アプリケーションのパフォーマンスを最適化することです。次に、プロファイリングが実際にパフォーマンスのボトルネックであることを示すコードの部分のみを最適化します。
構成の詳細に個別の最終クラスを使用できますか
はい、それは可能です。誰もあなたを止めるつもりはない1。しかし、それは悪い考えです。構成パラメーターを変更するためにコードを再コンパイルする必要があることを意味するものはすべて悪い考えです。 IMO。
構成ファイルからすべての構成詳細を一度に読み取り、それらをアプリケーションで後で使用するためにグローバル定数として保存する。
ああ...だから、「定数」の値をハードワイヤリングする代わりに読みたい。
はい、可能です。また、構成パラメータをコードに直接配線するよりも理にかなっています。しかし、それはまだ良い考えではありません(IMO)。
どうして?コードがどのように見えるかを見てみましょう。
public final class Config {
public static final int CONST_1;
public static final String CONST_2;
static {
int c1;
String c2;
try (Scanner s = new Scanner(new File("config.txt"))) {
c1 = s.nextInt();
c2 = s.next();
} catch (IOException ex) {
throw RuntimeException("Cannot load config properties", ex);
}
CONST_1 = c1;
CONST_2 = c2;
}
}
最初の観察では、クラスがfinal
であるという違いはありません。 fieldsをfinal
として宣言し、それらを定数にします。 (クラスをfinal
として宣言するとサブクラス化が防止されますが、static
フィールドには影響がありません。静的フィールドは継承の影響を受けません。)
次の観察は、このコードが多くの点で脆弱であることです:
静的初期化ブロックで何か問題が発生した場合。ブロックによってスローされる未チェックの例外は、
ExceptionInInitializerError
(はい...それはError
!!です)としてラップされ、Config
クラスがマークされます。誤りとして。それが起こった場合、回復する現実的な希望はなく、
Error
を試して診断するのはおそらく悪い考えです。上記のコードは
Config
クラスが初期化されると実行されますが、それがいつ起こるかを判断するのは難しい場合があります。構成ファイル名がパラメーターである場合、静的初期化がトリガーされる前にパラメーター値を保持するという問題が発生します。
次に、状態をインスタンス変数にロードするのに比べて、コードはかなり複雑です。そして、その混乱は、主に静的初期化子の制約内で作業しなければならない結果です。代わりにfinal
インスタンス変数を使用した場合のコードは次のとおりです。
public final class Config {
public final int CONST_1;
public final String CONST_2;
public Config(File file) throws IOException {
try (Scanner s = new Scanner(file)) {
CONST_1 = s.nextInt();
CONST_2 = s.next();
}
}
}
最後に、final
フィールドに対するstatic final
フィールドのパフォーマンス上の利点はわずかです。
定数の1つにアクセスするたびに、おそらく1つまたは2つの機械語命令、
jITコンパイラがスマートで、シングルトン
Config
参照を適切に処理する場合は、おそらく何もありません。
いずれの場合も、ほとんどの場合、利益はわずかです。
1-OK ...コードがコードレビューされている場合、おそらく誰かがあなたを止めるでしょう。
Apache commons configuration http://commons.Apache.org/proper/commons-configuration/ を聞いたことがありますか?これは私がこれまでに見つけた最高の構成リーダーであり、1年以来実稼働で実行されている私のアプリケーションでも使用しています。問題を発見したことはなく、非常にわかりやすく使いやすく、優れたパフォーマンスを発揮します。私はあなたのアプリケーションに少し依存していることを知っていますが、あなたがそれを好きになると信じてください。
あなたがする必要があるのは
Configuration config = new ConfigSelector().getPropertiesConfiguration(configFilePath);
String value = config.getString("key");
int value1 = config.getInt("key1");
String[] value2 = config.getStringArray("key2");
List<Object> value3 = config.getList("key3");
以上です。構成オブジェクトはすべての構成値を保持し、必要なだけ多くのクラスにそのオブジェクトを渡すことができます。非常に多くの有用なメソッドを使用して、必要なタイプのキーを抽出できます。
プロパティファイルに配置し、アプリケーションの開始時にファイルを読み取り、すべてのパラメーターをシステムパラメーターとして初期化する場合(System.setProperty)に定数を定義すると、1回限りのコストになりますあなたのコードのように
public static final String MY_CONST = System.getProperty("my.const");
ただし、他のクラスをロードする前に、アプリケーションの起動時に初期化を確認してください。
構成にはさまざまなタイプがあります。
通常、データベースやサービスへの接続など、何らかの種類のブートストラップ構成が、アプリケーションを起動できるようにするために必要です。データベース接続パラメーターを指定するJ2EEの方法は、コンテナーのJNDIレジストリ(Glassfish、JBoss、Websphereなど)で指定された「datasource」を使用する方法です。このデータソースは、persistence.xmlで名前で検索されます。非J2EEアプリケーションでは、これらをSpringコンテキストまたは.propertiesファイルで指定するのがより一般的です。いずれの場合でも、通常、アプリケーションを何らかのデータストアに接続するために何かが必要です。
データストアへのブートストラップ後、このデータストア内の構成値を管理するオプションがあります。たとえば、データベースがある場合は、設定値に別のテーブル(アプリケーションのJPAエンティティなどで表される)を使用できます。この柔軟性が必要ない場合は、代わりに単純な.propertiesファイルを使用できます。 Java( ResourceBundle )および Spring のようなフレームワークで.propertiesファイルを適切にサポートしています。VanillaResourceBundleはプロパティを1回だけロードします。 、Springヘルパーは、構成可能なキャッシュと再読み込みを提供します(これは、言及したパフォーマンスの側面に役立ちます)注:ファイルの代わりに、データストアによってバッキングされるプロパティを使用することもできます。
多くの場合、アプリケーションには両方のアプローチが共存します。デプロイされたアプリケーション内で決して変化しない値(アプリケーション名など)は、プロパティファイルから読み取ることができます。再デプロイせずに実行時にアプリケーションメンテナーが変更する必要がある可能性のある値(セッションタイムアウト間隔など)は、再読み込み可能な.propertiesファイルまたはデータベースに保存する方がよい場合があります。アプリケーションのユーザーが変更できる値は、アプリケーションのデータストアに保持する必要があり、通常はそれらを編集するためのアプリケーション内画面が必要です。
そのため、構成設定をカテゴリ(ブートストラップ、展開、ランタイム、アプリケーションなど)に分け、それらを管理する適切なメカニズムを選択することをお勧めします。これは、アプリケーションの範囲にも依存します。つまり、J2EE Webアプリ、デスクトップアプリ、コマンドラインユーティリティ、バッチプロセスですか。
さて、これは意志の中で一度すべての人の人生に直面している大きな問題です。問題になったので、これは、デフォルト値を持つ設定ファイルと同じインスタンス変数を持つシングルトンクラスを作成することで解決できます。第二に、このクラスにはgetInstance()
のようなメソッドが必要です。このメソッドは、プロパティを1回読み取り、存在する場合は常に同じオブジェクトを返します。ファイルを読み取るために、環境変数を使用してパスまたはSystem.getenv("Config_path");
などを取得できます。プロパティの読み取り(readProperties()
メソッド)は、構成ファイルから各項目を読み取り、値をシングルトンオブジェクトのインスタンス変数に設定する必要があります。そのため、1つのオブジェクトにすべての構成パラメーターの値が含まれ、パラメーターが空の場合はデフォルト値が考慮されます。
どのような構成ファイルを念頭に置いていますか?プロパティファイルの場合、次のようになります。
_public class Configuration {
// the configuration file is stored in the root of the class path as a .properties file
private static final String CONFIGURATION_FILE = "/configuration.properties";
private static final Properties properties;
// use static initializer to read the configuration file when the class is loaded
static {
properties = new Properties();
try (InputStream inputStream = Configuration.class.getResourceAsStream(CONFIGURATION_FILE)) {
properties.load(inputStream);
} catch (IOException e) {
throw new RuntimeException("Failed to read file " + CONFIGURATION_FILE, e);
}
}
public static Map<String, String> getConfiguration() {
// ugly workaround to get String as generics
Map temp = properties;
Map<String, String> map = new HashMap<String, String>(temp);
// prevent the returned configuration from being modified
return Collections.unmodifiableMap(map);
}
public static String getConfigurationValue(String key) {
return properties.getProperty(key);
}
// private constructor to prevent initialization
private Configuration() {
}
}
_
getConfiguration()
メソッドからすぐにProperties
オブジェクトを返すこともできますが、アクセスするコードによって変更される可能性があります。 Collections.unmodifiableMap()
は設定を行いませんconstant(Properties
インスタンスは作成後にload()
メソッドによって値を取得するため)ただし、変更不可マップにラップされているため、他のクラスによって構成を変更することはできません。
もう1つの方法は、クラスを定義し、そのクラスのプロパティファイルを読み取ることです。このクラスはアプリケーションレベルである必要があり、シングルトンとしてマークできます。クラスをシングルトンとしてマークすると、複数のインスタンスが作成されなくなります。
テキストベースのファイルで動作するJAXBまたは同様のバインディングフレームワークを使用することをお勧めします。 JAXB実装はJREの一部であるため、非常に使いやすいです。 As Denis 構成キーに対するアドバイス。
これは、XMLとJAXBを使用してアプリケーションを構成するための、使いやすく非常に強力な方法の簡単な例です。 DIフレームワークを使用する場合、DIコンテキストに同様の構成オブジェクトを追加するだけです。
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class ApplicationConfig {
private static final JAXBContext CONTEXT;
public static final ApplicationConfig INSTANCE;
// configuration properties with defaults
private int number = 0;
private String text = "default";
@XmlElementWrapper
@XmlElement(name = "text")
private List<String> texts = new ArrayList<>(Arrays.asList("default1", "default2"));
ApplicationConfig() {
}
static {
try {
CONTEXT = JAXBContext.newInstance(ApplicationConfig.class);
} catch (JAXBException ex) {
throw new IllegalStateException("JAXB context for " + ApplicationConfig.class + " unavailable.", ex);
}
File applicationConfigFile = new File(System.getProperty("config", new File(System.getProperty("user.dir"), "config.xml").toString()));
if (applicationConfigFile.exists()) {
INSTANCE = loadConfig(applicationConfigFile);
} else {
INSTANCE = new ApplicationConfig();
}
}
public int getNumber() {
return number;
}
public String getText() {
return text;
}
public List<String> getTexts() {
return Collections.unmodifiableList(texts);
}
public static ApplicationConfig loadConfig(File file) {
try {
return (ApplicationConfig) CONTEXT.createUnmarshaller().unmarshal(file);
} catch (JAXBException ex) {
throw new IllegalArgumentException("Could not load configuration from " + file + ".", ex);
}
}
// usage
public static void main(String[] args) {
System.out.println(ApplicationConfig.INSTANCE.getNumber());
System.out.println(ApplicationConfig.INSTANCE.getText());
System.out.println(ApplicationConfig.INSTANCE.getTexts());
}
}
構成ファイルは次のようになります。
<?xml version="1.0" encoding="UTF-8"?>
<applicationConfig>
<number>12</number>
<text>Test</text>
<texts>
<text>Test 1</text>
<text>Test 2</text>
</texts>
</applicationConfig>
構成キーをクラスに直接配置するのは悪いことです。構成キーはコード全体に散らばります。ベストプラクティスは、アプリケーションコードと構成コードの分離です。通常、springのような依存性注入フレームワークが使用されます。構成ファイルをロードし、構成値を使用してオブジェクトを構築します。クラスで設定値が必要な場合は、この値のセッターを作成する必要があります。 Springは、コンテキストの初期化中にこの値を設定します。
protected Java.util.Properties loadParams() throws IOException {
// Loads a ResourceBundle and creates Properties from it
Properties prop = new Properties();
URL propertiesFileURL = this.getClass().getResource("/conf/config.properties");
prop.load(new FileInputStream(new File(propertiesFileURL.getPath())));
return prop;
}
Properties prop = loadParams();
String prop1=(String) prop.get("x.y.z");