web-dev-qa-db-fra.com

Quelle est la meilleure façon de lire les paramètres de configuration du fichier de configuration en Java?

Supposons que jusqu'à l'exécution, nous ne connaissons pas les détails de la configuration (l'utilisateur peut avoir besoin de configurer ces paramètres dans le fichier config avant d'exécuter l'application.

Je souhaite lire ces détails de configuration et dois les réutiliser partout où j'en ai besoin dans mon application. Pour cela, je veux les faire sous forme de constantes globales (public static final).

Donc, mon doute est, y a-t-il des implications en termes de performances si je lis à partir du fichier config directement à partir de la classe requise? depuis, les valeurs d'exécution que je ne peux pas mettre directement dans un Interface séparé.

Je pense que cela aura un impact sur les performances. Veuillez me suggérer une meilleure façon de le faire.

PDATE: Puis-je utiliser une classe finale distincte pour les détails de configuration? mettre tous les détails de configuration sous forme de constantes dans un public final class
(Pour lire tous les détails de configuration à la fois dans le fichier de configuration et les stocker en tant que constantes globales pour une utilisation ultérieure dans l'application)

20
Sagar Pudi

Je pense que cela aura un impact sur les performances.

Je doute que ce soit vrai.

En supposant que l'application ne lit le fichier de configuration qu'une seule fois au démarrage, le temps nécessaire pour lire le fichier n'est probablement pas pertinent pour les performances globales de votre application. En effet, plus l'application est longue, moins le temps de démarrage sera important.

Le conseil standard consiste à optimiser les performances des applications uniquement lorsque vous avez des preuves concrètes (c'est-à-dire des mesures) pour dire que les performances est un problème important. Ensuite, optimisez uniquement les parties de votre code dont le profilage vous indique qu'elles sont vraiment un goulot d'étranglement des performances.


Puis-je utiliser une classe finale distincte pour les détails de configuration

Oui, c'est possible. Personne ne va t'arrêter1. Cependant, c'est une mauvaise idée. Tout ce qui signifie que vous devez recompiler votre code pour modifier les paramètres de configuration est une mauvaise idée. OMI.


Pour lire tous les détails de configuration à la fois à partir du fichier de configuration et les stocker en tant que constantes globales pour une utilisation ultérieure dans l'application.

Ah ... alors je veux lire les valeurs des "constantes" au lieu de les câbler.

Oui, c'est possible. Et cela a plus de sens que les paramètres de configuration de câblage en dur dans le code. Mais ce n'est toujours pas une bonne idée (OMI).

Pourquoi? Voyons à quoi le code doit ressembler:

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; 
    }
}

La première observation est que cela ne fait aucune différence que la classe est final. Il déclare les champs comme final qui les rend constants. (La déclaration de la classe comme final empêche le sous-classement, mais cela n'a aucun impact sur les champs static. Les champs statiques ne sont pas affectés par l'héritage.)

L'observation suivante est que ce code est fragile à plusieurs égards:

  • Si quelque chose ne va pas dans le bloc d'initialisation statique. l'exception non vérifiée qui est levée par le bloc sera enveloppée sous la forme d'un ExceptionInInitializerError (oui ... il s'agit d'un Error !!), et la classe Config sera marquée comme erroné.

  • Si cela se produit, il n'y a aucun espoir réaliste de récupérer, et c'est peut-être même une mauvaise idée d'essayer de diagnostiquer le Error.

  • Le code ci-dessus est exécuté lorsque la classe Config est initialisée, mais déterminer quand cela se produit peut être délicat.

  • Si le nom de fichier de configuration est un paramètre, vous avez alors le problème d'obtenir la valeur du paramètre ... avant que l'initialisation statique ne soit déclenchée.

Ensuite, le code est assez compliqué par rapport au chargement de l'état dans une variable d'instance. Et ce désordre est en grande partie le résultat de devoir travailler dans les limites des initialiseurs statiques. Voici à quoi ressemble le code si vous utilisez des variables d'instance final à la place.

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();
        } 
    }
}

Enfin, les avantages de performance de static final les champs sur final les champs sont minuscules:

  • probablement une ou deux instructions machine à chaque fois que vous accédez à l'une des constantes,

  • éventuellement rien du tout si le compilateur JIT est intelligent et que vous gérez la référence singleton Config de manière appropriée.

Dans les deux cas, dans la grande majorité des cas, les avantages seront insignifiants.


1 - OK ... si votre code est révisé, alors quelqu'un vous arrêtera probablement.

21
Stephen C

Avez-vous déjà entendu parler de la configuration d'Apache commons http://commons.Apache.org/proper/commons-configuration/ ? C'est le meilleur lecteur de configuration que j'ai jamais trouvé et je l'utilise même dans mon application qui fonctionne en production depuis 1 an. Jamais trouvé de problèmes, très facile à comprendre et à utiliser, d'excellentes performances. Je sais que c'est un peu dépendant de votre application mais croyez-moi, vous l'aimerez.

Tout ce que vous devez faire c'est

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");

Et c'est tout. Votre objet de configuration contiendra toutes les valeurs de configuration et vous pouvez simplement passer cet objet à autant de classes que vous le souhaitez. Avec autant de méthodes utiles disponibles, vous pouvez extraire le type de clé que vous souhaitez.

20
Sri777

Ce ne sera qu'un coût unique si vous les placez dans un fichier de propriétés et lisez le fichier au début de votre application et initialisez tous les paramètres en tant que paramètres système (System.setProperty) puis définissez des constantes dans votre code comme

public static final String MY_CONST = System.getProperty("my.const");

Mais assurez-vous de l'initialisation au démarrage de votre application avant de charger toute autre classe.

5
Sanjeev Kumar

Il existe différents types de configuration.

Habituellement, une sorte de configuration d'amorçage, par exemple pour se connecter à une base de données ou à un service, est nécessaire pour pouvoir démarrer l'application. La manière J2EE de spécifier les paramètres de connexion à la base de données est via une 'source de données' spécifiée dans le registre JNDI de votre conteneur (Glassfish, JBoss, Websphere, ...). Cette source de données est ensuite recherchée par nom dans votre persistence.xml. Dans les applications non J2EE, il est plus courant de les spécifier dans un contexte Spring ou même dans un fichier .properties. Dans tous les cas, vous avez généralement besoin de quelque chose pour connecter votre application à une sorte de magasin de données.

Après le démarrage d'un magasin de données, une option consiste à gérer les valeurs de configuration à l'intérieur de ce magasin de données. Par exemple, si vous avez une base de données, vous pouvez utiliser une table distincte (représentée par exemple par une entité JPA dans votre application) pour les valeurs de configuration. Si vous ne voulez pas/n'avez pas besoin de cette flexibilité, vous pouvez utiliser à la place un fichier .properties simple. Il existe un bon support pour les fichiers .properties dans Java ( ResourceBundle ) et dans des frameworks comme Spring . Le Vanilla ResourceBundle charge juste les propriétés une fois , l'assistant Spring propose une mise en cache et un rechargement configurables (cela contribue à l'aspect des performances que vous avez mentionné) Remarque: vous pouvez également utiliser des propriétés sauvegardées par un magasin de données au lieu d'un fichier.

Souvent, les deux approches coexistent dans une application. Les valeurs qui ne changent jamais dans une application déployée (comme le nom de l'application) peuvent être lues à partir d'un fichier de propriétés. Les valeurs qui pourraient devoir être modifiées par un responsable de l'application au moment de l'exécution sans redéploiement (par exemple, l'intervalle de temporisation de la session) pourraient mieux être conservées dans un fichier .properties rechargeable ou dans une base de données. Les valeurs qui peuvent être modifiées par les utilisateurs de l'application doivent être conservées dans le magasin de données de l'application et avoir généralement un écran intégré à l'application pour les modifier.

Je vous conseille donc de séparer vos paramètres de configuration en catégories (par exemple, bootstrap, déploiement, runtime et application) et de sélectionner un mécanisme approprié pour les gérer. Cela dépend également de la portée de votre application, c'est-à-dire qu'il s'agit d'une application Web J2EE, d'une application de bureau, d'un utilitaire de ligne de commande, d'un processus par lots?

5
Adriaan Koster

Eh bien, c'est un grand problème qui se pose dans la vie de chacun une fois par testament. Pour en venir au problème, cela peut être résolu en créant une classe singleton qui a des variables d'instance identiques à celles du fichier de configuration avec des valeurs par défaut. Deuxièmement, cette classe devrait avoir une méthode comme getInstance() qui lit les propriétés une fois et chaque fois retourne le même objet s'il existe. Pour lire un fichier, nous pouvons utiliser la variable environnementale pour obtenir le chemin ou quelque chose comme System.getenv("Config_path");. La lecture des propriétés (méthode readProperties()) doit lire chaque élément du fichier de configuration et définir la valeur sur les variables d'instance de l'objet singleton. Alors maintenant, un seul objet contient toute la valeur du paramètre de configuration et aussi si le paramètre est vide, la valeur par défaut est prise en compte.

4
Bhargav Modi

À quel type de fichier de configuration pensez-vous? S'il s'agit d'un fichier de propriétés, cela pourrait vous convenir:

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() {
    }

}

Vous pouvez également renvoyer l'objet Properties immédiatement à partir de la méthode getConfiguration(), mais il pourrait ensuite être modifié par le code qui y accède. La Collections.unmodifiableMap() ne fait pas la configuration constante (puisque l'instance Properties obtient ses valeurs par la méthode load() après sa création), cependant, comme il est enveloppé dans une carte non modifiable, la configuration ne peut pas être modifiée par d'autres classes.

4
matsev

Une autre façon consiste à définir une classe et à lire le fichier de propriétés de cette classe. Cette classe doit être au niveau de l'application et peut être marquée comme singleton. Marquer la classe comme Singleton évitera la création de plusieurs instances.

2
Yusuf Kapasi

Je recommande d'utiliser JAXB ou un cadre de liaison similaire qui fonctionne avec des fichiers texte. Puisqu'une implémentation JAXB fait partie du JRE, elle est assez facile à utiliser. Comme Denis je déconseille les clés de configuration.

Voici un exemple simple pour un moyen facile à utiliser et encore assez puissant de configurer votre application avec XML et JAXB. Lorsque vous utilisez un framework DI, vous pouvez simplement ajouter un objet de configuration similaire au contexte 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());
    }
}

Le fichier de configuration ressemble à ceci:

<?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>
1
chromanoid

Placer les clés de configuration directement dans les classes est mauvais: les clés de configuration seront dispersées sur le code. La meilleure pratique consiste à séparer le code d'application et le code de configuration. Habituellement, un cadre d'injection de dépendance comme le ressort est utilisé. Il charge un fichier de configuration et construit les objets à l'aide de valeurs de configuration. Si vous avez besoin d'une valeur de configuration dans votre classe, vous devez créer un setter pour cette valeur. Spring définira cette valeur lors de l'initialisation du contexte.

0
Denis Borovikov
  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");
0
Sebri Zouhaier