web-dev-qa-db-fra.com

Printemps injectant un singleton statique (global)

J'ai une classe qui ressemble à ceci:

public class Configurator {
    private static Configurator INSTANCE = null;

    private int maxRange = 1;

    // many other properties; each property has a default value

    private static synchronized Configurator getInstance() {
        if(INSTANCE == null)
            return new Configurator();

        return INSTANCE;
    }

    public static int getMaxRange() {
        getInstance().maxRange;
    }

    public static void setMaxRange(int range) {
        getInstance().maxRange = range;
    }

    // Getters and setters for all properties follow this pattern
}

Il sert d'objet de configuration globale pouvant être défini au démarrage de l'application, puis utilisé par des dizaines de classes au cours du projet:

// Called at app startup to configure everything
public class AppRunner {
    Configurator.setMaxRange(30);
}

// Example of Configurator being used by another class
public class WidgetFactory {
    public void doSomething() {
        if(Configurator.getMaxRange() < 50)
            // do A
        else
            // do B
    }
}

J'importe maintenant ce code dans un projet Spring et j'essaie de configurer mon Sprinig XML (beans). Je suppose que je pourrais définir un haricot Configurator solitaire comme suit (ou quelque chose de similaire):

<bean id="configurator" class="com.me.myapp.Configurator" scope="singleton">
    <property name="maxRange" value="30"/>
    <!-- etc., for all properties -->
</bean>

Ainsi, lorsque WidgetFactory#doSomething sera exécuté, Spring aura déjà chargé la classe Configurator et l’a configurée à l’avance.

Est-il correct de définir le scope="singleton" ou est-ce que cela n'a pas d'importance? Est-ce que je configure correctement les propriétés statiques? Y at-il autre chose que je dois faire ou considérer ici? Merci d'avance.

11
user1768830

Il existe certaines différences entre Singleton en tant que modèle de conception et les installations de Spring de Singleton. Singleton, en tant que modèle de conception, garantit que vous avez défini un objet de classe par chargeur de classe. L'installation singleton de Spring (et son approche), en revanche, définiront une instance par contexte Spring. 

Dans ce cas, vous pouvez utiliser votre méthode getInstance() à utiliser par Spring pour saisir votre instance d'objet:

<bean id="configurator" class="com.me.myapp.Configurator" factory-method="getInstance">
</bean>

Avec Spring, la portée du bean singleton est la valeur par défaut; vous n'avez donc pas besoin de la définir.

Si vous voulez utiliser configurator comme bean Spring, vous devrez l'injecter dans d'autres objets et non utiliser getInstance() pour le récupérer. Donc, dans d’autres beans Spring, utilisez @Autowired ou définissez une référence à bean via un fichier xml. Si vous ne réorganisez pas l'utilisation de configurator dans d'autres classes, il n'y aura aucune différence, Spring instanciera votre classe, mais vous l'utiliserez comme auparavant.

J'ai aussi vu que vous avez une erreur dans la conception de votre singleton. Votre méthode getInstance() doit être publique et les autres méthodes ne doivent pas être statiques. Dans l'exemple que vous avez utilisé, vous devriez utiliser Singleton comme ceci:

Configurator.getInstance().someMethod()

Dans ce cas, vous utilisez simplement la classe Singleton, sans instancier d'objets! Voir wikipedia sur Singleton (avec l'exemple de Java) pour plus d'informations sur le modèle de conception Singleton et sur son utilisation.


REMARQUE: Cela vaut la peine de connaître et d'essayer d'utiliser Configurator comme Singleton et d'utiliser les fonctions de singleton de Spring. Si vous faites cela, les avantages seront que vous pouvez

  • Supprimer la méthode getInstance() 
  • Rendez votre constructeur public 
  • Laissez le printemps instancier cet objet unique.
10
partlov

Les haricots sont singleton par défaut. Vous pouvez trouver cela/plus d'informations sur le site Web du printemps.

Vous ne devez pas instancier un nouveau configurateur dans getInstance car il ne fera pas référence au bean chargé par ressort, ce qui pourrait entraîner des problèmes graves. Vous pouvez câbler ce haricot et le laisser ensuite, il ne sera pas nul parce que vous l'avez câblé (et si c'est votre programme, l'initialisation a échoué).

1
Bob Flannigon

Oui, si vous voulez quelque chose de global, singleton scope est la bonne option. Voici quelques points qui méritent d’être mentionnés: 

  1. L'étendue par défaut au printemps est singleton, vous n'avez donc pas besoin de Définir explicitement votre bean en tant qu'étendue singleton.
  2. Avec Spring, vous n'avez pas besoin d'écrire Singleton pattern code de style, tels que des instances privées et des méthodes d'usine. En effet, Spring Garantira qu’il n’ya qu’une seule instance par conteneur Spring . Même pas à dire, votre méthode d'usine est privée.
0
spiritwalker

Au fait: ce n'est pas thread-safe:

if(INSTANCE == null)
        return new Configurator();

    return INSTANCE;
}

Considérant que ce serait:

private static Configurator INSTANCE = new Configurator();

(Initialisation désirée)

private static volatile Singleton _instance = null;

(Initialisation paresseuse avec mot clé volatile)

Cela concerne la façon dont Java alloue de la mémoire et crée des instances. Ce n'est pas atomique, mais fait en deux étapes et peut être interféré par le planificateur de thread.

Voir aussi http://regrecall.blogspot.de/2012/05/Java-singleton-pattern-thread-safe.html .

0
Dennis Löhmann