Modèle de conception Singleton vs beans Singleton dans le conteneur Spring

90

Comme nous le savons tous, nous avons des beans en tant que singleton par défaut dans le conteneur Spring et si nous avons une application Web basée sur le framework Spring, dans ce cas, nous devons vraiment implémenter le modèle de conception Singleton pour contenir des données globales plutôt que de simplement créer un bean à travers le printemps .

Veuillez me supporter si je ne suis pas en mesure d'expliquer ce que je voulais vraiment demander.

Peeyush
la source

Réponses:

60

Un haricot singleton au printemps et le motif singleton sont assez différents. Le modèle Singleton indique qu'une et une seule instance d'une classe particulière sera créée par chargeur de classe.

La portée d'un singleton Spring est décrite comme "par conteneur par bean". Il s'agit de la portée de la définition du bean vers une seule instance d'objet par conteneur Spring IoC. La portée par défaut dans Spring est Singleton.

Même si la portée par défaut est singleton, vous pouvez modifier la portée du bean en spécifiant l'attribut scope de l' <bean ../>élément.

<bean id=".." class=".." scope="prototype" />
user184794
la source
12
@ user184794: par conteneur par bean, ce qui signifie qu'il n'y a qu'un seul chargeur de classe dans le conteneur de printemps.Si il y a deux chargeurs de classe ou plus dans le conteneur de printemps, alors chaque chargeur de classe aura sa propre instance. Cela veut-il dire "par conteneur par classloader par bean". veuillez clarifier !!
Dead Programmer
4
Je pense que cela signifie qu'un conteneur Spring utilisera un seul chargeur de classe qu'il possède. ce que vous faites en dehors du mécanisme de Spring n'est pas pertinent, c'est-à-dire que vous pouvez créer vos propres chargeurs de classe et créer autant d'instances d'une classe que vous le souhaitez, mais si vous passez par le conteneur Spring, il ne créera pas plus d'une instance
inor
1
Alors ils ne sont pas «tout à fait différents» comme vous le dites. La seule différence est la portée - Spring container verses classloader
Zack Macomber
30

La portée singleton dans spring signifie une seule instance dans un contexte Spring.
Le conteneur Spring renvoie simplement la même instance encore et encore pour les appels suivants pour obtenir le bean.


Et spring ne dérange pas si la classe du bean est codée comme singleton ou non, en fait si la classe est codée comme singleton dont le constructeur est privé, Spring utilise BeanUtils.instantiateClass (javadoc ici ) pour définir le constructeur sur accessible et invoquer il.

Alternativement, nous pouvons utiliser un attribut de méthode d'usine dans la définition de bean comme ceci

    <bean id="exampleBean" class="example.Singleton"  factory-method="getInstance"/>
Vasanth
la source
1
êtes-vous sûr d'avoir besoin de l'attribut factory-method? Je suis presque sûr que Spring sait comment obtenir une instance même si le constructeur est privé (essaie probablement d'appeler getInstance)
inor
Une discussion connexe sur la façon dont Spring invoque le constructeur privé ici
Xiawei Zhang
21

Prenons l'exemple le plus simple: vous avez une application et vous utilisez simplement le chargeur de classe par défaut. Vous avez une classe qui, pour une raison quelconque, vous décidez qu'elle ne doit pas avoir plus d'une instance dans l'application. (Pensez à un scénario où plusieurs personnes travaillent sur des pièces de l'application).

Si vous n'utilisez pas le framework Spring, le modèle Singleton garantit qu'il n'y aura pas plus d'une instance d'une classe dans votre application. C'est parce que vous ne pouvez pas instancier des instances de la classe en faisant 'new' car le constructeur est privé. La seule façon d'obtenir une instance de la classe est d'appeler une méthode statique de la classe (généralement appelée «getInstance») qui renvoie toujours la même instance.

Dire que vous utilisez le framework Spring dans votre application signifie simplement qu'en plus des moyens habituels d'obtenir une instance de la classe (méthodes nouvelles ou statiques qui renvoient une instance de la classe), vous pouvez également demander à Spring de vous obtenir une instance de cette classe et Spring garantira que chaque fois que vous lui demanderez une instance de cette classe, elle retournera toujours la même instance, même si vous n'avez pas écrit la classe en utilisant le modèle Singleton. En d'autres termes, même si la classe a un constructeur public, si vous demandez toujours à Spring une instance de cette classe, Spring n'appellera ce constructeur qu'une seule fois pendant la durée de vie de votre application.

Normalement, si vous utilisez Spring, vous ne devez utiliser Spring que pour créer des instances, et vous pouvez avoir un constructeur public pour la classe. Mais si votre constructeur n'est pas privé, vous n'empêchez vraiment personne de créer directement de nouvelles instances de la classe, en contournant Spring.

Si vous voulez vraiment une seule instance de la classe, même si vous utilisez Spring dans votre application et que vous définissez la classe dans Spring comme un singleton, le seul moyen de garantir cela est également d'implémenter la classe à l'aide du modèle Singleton. Cela garantit qu'il y aura une seule instance, que les gens utilisent Spring pour obtenir une instance ou contourner Spring.

inor
la source
13

Je trouve " par contenant par haricot" difficile à appréhender . Je dirais " un bean par id de haricot dans un conteneur ". Prenons un exemple pour le comprendre. Nous avons un exemple de classe de haricots. J'ai défini deux beans de cette classe dans la définition de bean, comme:

<bean id="id1" class="com.example.Sample" scope="singleton">
        <property name="name" value="James Bond 001"/>    
</bean>    
<bean id="id7" class="com.example.Sample" scope="singleton">
        <property name="name" value="James Bond 007"/>    
</bean>

Ainsi, chaque fois que j'essaye d'obtenir le bean avec l'id "id1", le conteneur de ressort créera un bean, le mettra en cache et retournera le même bean où jamais référencé avec id1. Si j'essaie de l'obtenir avec id7, un autre bean sera créé à partir de la classe Sample, il sera mis en cache et retourné chaque fois que vous l'avez référencé avec id7.

Ceci est peu probable avec le modèle Singleton. Dans le modèle Singlton, un objet par chargeur de classe est toujours créé. Cependant, au printemps, faire de la portée comme Singleton n'empêche pas le conteneur de créer de nombreuses instances à partir de cette classe. Cela restreint simplement la création d'un nouvel objet pour le même ID, retournant un objet précédemment créé lorsqu'un objet est demandé pour le même ID . Référence

Dexter
la source
Bien expliqué. Merci!
Swapnil le
12

La portée Singleton dans Spring signifie que ce bean ne sera instancié qu'une seule fois par Spring. Contrairement à la portée du prototype (nouvelle instance à chaque fois), la portée de la requête (une fois par requête), la portée de la session (une fois par session HTTP).

La portée Singleton n'a techniquement rien à voir avec le modèle de conception singleton. Vous n'avez pas besoin d'implémenter vos beans en tant que singletons pour qu'ils soient placés dans la portée singleton.

Lexicore
la source
1
Corrigez-moi si je me trompe donc selon vous, si je dois implémenter un objet en tant que singleton, il n'est donc pas nécessaire d'implémenter un modèle de singleton. Créer ce bean à l'aide de Spring fonctionnera. Je suis un peu confus maintenant avec ma compréhension du modèle de conception Singleton et de la portée de Singleton dans le framework Spring.
Peeyush
1
Spring ne vous oblige pas à utiliser le modèle Singleton.
lexicore
2

Les beans singleton dans Spring et les classes basées sur le modèle de conception Singleton sont assez différents.

Le modèle Singleton garantit qu'une et une seule instance d'une classe particulière sera jamais créée par chargeur de classe, alors que la portée d'un bean singleton Spring est décrite comme «par conteneur par bean». La portée Singleton dans Spring signifie que ce bean ne sera instancié qu'une seule fois par Spring. Le conteneur Spring renvoie simplement la même instance encore et encore pour les appels suivants pour obtenir le bean.

JavaMave
la source
13
Vous êtes le «non-conformiste de Java», non? Cela ferait de votre déclaration, "Trouvé une bonne explication et un bon exemple à ..." une tentative malhonnête de cacher que vous créez un lien vers votre propre site Web. Votre lien ne semble pas être important pour la réponse, de toute façon. Je le supprime, pour éviter que la réponse ne soit supprimée comme spam. Veuillez lire la FAQ sur l'auto-promotion avant de publier d'autres liens vers votre site Web. notez également qu'il est très bien pour vous de mettre le lien de votre site Web dans votre profil.
Andrew Barber
2

Il y a une différence très fondamentale entre les deux. Dans le cas du modèle de conception Singleton, une seule instance d'une classe sera créée par classLoader alors que ce n'est pas le cas avec Spring singleton, car dans la dernière instance de bean partagé pour l'ID donné par conteneur IoC est créée.

Par exemple, si j'ai une classe avec le nom "SpringTest" et que mon fichier XML ressemble à ceci: -

<bean id="test1" class="com.SpringTest" scope="singleton">
        --some properties here
</bean>    
<bean id="test2" class="com.SpringTest" scope="singleton">
        --some properties here   
</bean>

Alors maintenant, dans la classe principale, si vous vérifiez la référence des deux ci-dessus, il retournera false comme selon la documentation de Spring: -

Lorsqu'un bean est un singleton, une seule instance partagée du bean sera gérée, et toutes les demandes de beans avec un ou des identifiants correspondant à cette définition de bean entraîneront le retour de cette instance de bean spécifique par le conteneur Spring.

Donc, comme dans notre cas, les classes sont les mêmes mais les identifiants que nous avons fournis sont différents, ce qui entraîne la création de deux instances différentes.

hhhakki
la source
1

"singleton" au printemps utilise l'instance get bean factory, puis la cache; quel modèle de conception de singleton est strictement, l'instance ne peut être récupérée qu'à partir de la méthode get statique, et l'objet ne peut jamais être instancié publiquement.

lwpro2
la source
1

EX: "par contenant par grain".

        <bean id="myBean" class="com.spring4hibernate4.TestBean">
            <constructor-arg name="i" value="1"></constructor-arg>
            <property name="name" value="1-name"></property>
        </bean>

        <bean id="testBean" class="com.spring4hibernate4.TestBean">
            <constructor-arg name="i" value="10"></constructor-arg>
            <property name="name" value="10-name"></property>
        </bean>
    </beans>



    public class Test {

        @SuppressWarnings("resource")
        public static void main(String[] args) {
            ApplicationContext ac = new ClassPathXmlApplicationContext("ws.xml");
            TestBean teatBean = (TestBean) ac.getBean("testBean");
            TestBean myBean1 = (TestBean) ac.getBean("myBean");
            System.out.println("a : " + teatBean.test + " : "   + teatBean.getName());
            teatBean.setName("a TEST BEAN 1");
            System.out.println("uPdate : " + teatBean.test + " : "  + teatBean.getName());
            System.out.println("a1 : " + myBean1.test + " : " + myBean1.getName());
            myBean1.setName(" a1 TEST BEAN 10");
            System.out.println("a1 update : " + teatBean.test + " : " + myBean1.getName());
        }
    }

public class TestBean {
    public int test = 0;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    private String name = "default";

    public TestBean(int i) {
        test += i;
    }
}

JAVA SINGLETON:

public class Singleton {
    private static Singleton singleton = new Singleton();
    private int i = 0;

    private Singleton() {
    }

    public static Singleton returnSingleton() {

        return singleton;
    }

    public void increment() {
        i++;
    }

    public int getInt() {
        return i;
    }
}

public static void main(String[] args) {
        System.out.println("Test");

        Singleton sin1 = Singleton.returnSingleton();
        sin1.increment();
        System.out.println(sin1.getInt());
        Singleton sin2 = Singleton.returnSingleton();
        System.out.println("Test");
        sin1.increment();
        System.out.println(sin1.getInt());
    }
Hariprasad
la source
<bean class = "com.spring4hibernate4.TestBean"> <constructor-arg name = "i" value = "1"> </constructor-arg> <property name = "name" value = "1-name"> </ property> </bean> <bean class = "com.spring4hibernate4.TestBean"> <constructor-arg name = "i" value = "10"> </constructor-arg> <property name = "name" value = "10 -name "> </property> </bean> </beans>
Hariprasad
1

Le haricot singleton de printemps est décrit comme «par contenant par haricot». La portée Singleton dans Spring signifie que le même objet au même emplacement mémoire sera renvoyé au même identifiant de bean. Si l'on crée plusieurs beans de différents identifiants de la même classe, le conteneur renverra différents objets à différents identifiants. C'est comme un mappage de valeur de clé où la clé est l'id du bean et la valeur est l'objet du bean dans un conteneur à ressort. Where as Singleton pattern garantit qu'une et une seule instance d'une classe particulière sera jamais créée par chargeur de classe.

Sibaprasad Sahoo
la source
1

Toutes les réponses, du moins jusqu'à présent, se concentrent sur l'explication de la différence entre le modèle de conception et Spring singleton et ne répondent pas à votre question réelle: un modèle de conception Singleton devrait-il être utilisé ou un haricot singleton Spring? Qu'est-ce qui est mieux?

Avant de répondre, permettez-moi de dire que vous pouvez faire les deux. Vous pouvez implémenter le bean en tant que modèle de conception Singleton et utiliser Spring pour l'injecter dans les classes clientes en tant que bean singleton Spring.

Maintenant, la réponse à la question est simple: n'utilisez pas le modèle de conception Singleton!
Utilisez le bean singleton de Spring implémenté en tant que classe avec un constructeur public.
Pourquoi? Parce que le modèle de conception Singleton est considéré comme un anti-modèle. Surtout parce que cela complique les tests. (Et si vous n'utilisez pas Spring pour l'injecter, toutes les classes qui utilisent le singleton y sont maintenant étroitement liées), et vous ne pouvez pas le remplacer ou l'étendre. On peut google "Anti-pattern Singleton" pour obtenir plus d'informations à ce sujet, par exemple anti-pattern Singleton

Utiliser Spring singleton est la voie à suivre (avec un bean singleton implémenté NON comme un modèle de conception Singleton, mais plutôt avec un constructeur public) afin que le bean Spring singleton puisse facilement être testé et les classes qui l'utilisent ne lui sont pas étroitement couplées , mais plutôt, Spring injecte le singleton (en tant qu'interface) dans tous les beans qui en ont besoin, et le bean singleton peut être remplacé à tout moment par une autre implémentation sans affecter les classes clientes qui l'utilisent.

inor
la source