Patrón de diseño Singleton vs frijoles Singleton en contenedor Spring

90

Como todos sabemos, tenemos beans como singleton de forma predeterminada en el contenedor Spring y si tenemos una aplicación web basada en el marco Spring, en ese caso, ¿realmente necesitamos implementar el patrón de diseño Singleton para contener datos globales en lugar de simplemente crear un bean a través de Spring? .

Por favor, tengan paciencia conmigo si no puedo explicar lo que realmente quería preguntar.

Peeyush
fuente

Respuestas:

60

Un frijol singleton en Spring y el patrón singleton son bastante diferentes. El patrón Singleton dice que una sola instancia de una clase particular será creada por cargador de clases.

El alcance de un singleton Spring se describe como "por contenedor por frijol". Es el alcance de la definición de bean para una sola instancia de objeto por contenedor Spring IoC. El alcance predeterminado en Spring es Singleton.

Aunque el alcance predeterminado es singleton, puede cambiar el alcance del bean especificando el atributo de alcance del <bean ../>elemento.

<bean id=".." class=".." scope="prototype" />
user184794
fuente
12
@ user184794: por contenedor por bean, lo que significa que solo hay un cargador de clases en el contenedor de primavera. Si hay dos o más cargadores de clases en el contenedor de resorte, entonces cada cargador de clases tendrá su propia instancia. ¿Significa "por contenedor por cargador de clases por bean"? ¡¡la amabilidad de aclarar!!
Programador muerto
4
Creo que significa que un contenedor Spring usará un solo cargador de clases que posee. lo que haga fuera del mecanismo de Spring no es relevante, es decir, puede crear sus propios cargadores de clases y crear tantas instancias de una clase como desee, pero si pasa por el contenedor de Spring, no creará más de una instancia
o
1
Entonces no son "muy diferentes" como usted afirma. La única diferencia es el alcance - Contenedor de primavera frente al cargador de clases
Zack Macomber
30

El alcance de singleton en Spring significa instancia única en un contexto de Spring.
El contenedor de Spring simplemente devuelve la misma instancia una y otra vez para las llamadas posteriores para obtener el bean.


Y Spring no se preocupa si la clase del bean está codificada como singleton o no, de hecho, si la clase está codificada como singleton cuyo constructor es privado, Spring usa BeanUtils.instantiateClass (javadoc aquí ) para configurar el constructor como accesible e invocar eso.

Alternativamente, podemos usar un atributo de método de fábrica en la definición de bean como este

    <bean id="exampleBean" class="example.Singleton"  factory-method="getInstance"/>
Vasanth
fuente
1
¿Estás seguro de que necesitas el atributo de método de fábrica? Estoy bastante seguro de que Spring sabe cómo obtener una instancia incluso si el constructor es privado (probablemente intente llamar a getInstance)
2014
Una discusión relacionada sobre cómo Spring invoca al constructor privado aquí
Xiawei Zhang
21

Tomemos el ejemplo más simple: tienes una aplicación y solo usas el cargador de clases predeterminado. Tienes una clase que, por el motivo que sea, decides que no debe tener más de una instancia en la aplicación. (Piense en un escenario en el que varias personas trabajen en partes de la aplicación).

Si no está utilizando el marco Spring, el patrón Singleton asegura que no habrá más de una instancia de una clase en su aplicación. Esto se debe a que no puede crear instancias de la clase haciendo 'nuevo' porque el constructor es privado. La única forma de obtener una instancia de la clase es llamar a algún método estático de la clase (generalmente llamado 'getInstance') que siempre devuelve la misma instancia.

Decir que está utilizando el marco Spring en su aplicación, solo significa que, además de las formas habituales de obtener una instancia de la clase (métodos nuevos o estáticos que devuelven una instancia de la clase), también puede pedirle a Spring que lo obtenga una instancia de esa clase y Spring se asegurarán de que cada vez que le pida una instancia de esa clase, siempre devolverá la misma instancia, incluso si no escribió la clase usando el patrón Singleton. En otras palabras, incluso si la clase tiene un constructor público, si siempre le pide a Spring una instancia de esa clase, Spring solo llamará a ese constructor una vez durante la vida de su aplicación.

Normalmente, si está usando Spring, solo debe usar Spring para crear instancias, y puede tener un constructor público para la clase. Pero si su constructor no es privado, en realidad no está evitando que nadie cree nuevas instancias de la clase directamente, sin pasar por Spring.

Si realmente desea una única instancia de la clase, incluso si usa Spring en su aplicación y define la clase en Spring para que sea un singleton, la única forma de asegurarse de eso es también implementar la clase usando el patrón Singleton. Eso asegura que habrá una sola instancia, ya sea que las personas usen Spring para obtener una instancia o eviten Spring.

En o
fuente
13

Encuentro " por contenedor por frijol" difícil de aprehender . Yo diría " un bean por id de bean en un contenedor ". Vamos a tener un ejemplo para entenderlo. Tenemos una clase de frijol Sample. He definido dos beans de esta clase en la definición de bean, como:

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

Entonces, cuando intento obtener el bean con id "id1", el contenedor de primavera creará un bean, lo almacenará en caché y devolverá el mismo bean donde se haya referido con id1. Si trato de obtenerlo con id7, se creará otro bean a partir de la clase Sample, el mismo se almacenará en caché y se devolverá cada vez que lo refiera con id7.

Esto es poco probable con el patrón Singleton. En el patrón Singlton, siempre se crea un objeto por cargador de clases. Sin embargo, en Spring, hacer que el alcance sea Singleton no restringe que el contenedor cree muchas instancias de esa clase. Simplemente restringe la creación de nuevos objetos para el mismo ID nuevamente, devolviendo el objeto creado anteriormente cuando se solicita un objeto para el mismo ID . Referencia

Diestro
fuente
Bien explicado. ¡Gracias!
Swapnil
12

El alcance de Singleton en Spring significa que Spring instanciará este bean solo una vez. A diferencia del alcance del prototipo (nueva instancia cada vez), alcance de la solicitud (una vez por solicitud), alcance de la sesión (una vez por sesión HTTP).

El visor singleton técnicamente no tiene nada que ver con el patrón de diseño singleton. No tiene que implementar sus beans como singleton para que se coloquen en el alcance singleton.

lexicore
fuente
1
Corríjame si me equivoco, de acuerdo con usted, si necesito implementar cualquier objeto como singleton, por lo que no es necesario implementar el patrón singleton. Crear ese bean usando Spring funcionará. Ahora estoy un poco confundido con mi comprensión relacionada con el patrón de diseño Singleton y el alcance Singleton en el marco Spring.
Peeyush
1
Spring no te obliga a usar el patrón Singleton.
lexicore
2

Los frijoles Singleton en Spring y las clases basadas en el patrón de diseño Singleton son bastante diferentes.

El patrón Singleton garantiza que se creará una y solo una instancia de una clase en particular por cargador de clases, mientras que el alcance de un bean singleton de Spring se describe como 'por contenedor por bean'. El alcance de Singleton en Spring significa que Spring instanciará este bean solo una vez. Spring container simplemente devuelve la misma instancia una y otra vez para llamadas posteriores para obtener el bean.

JavaMave
fuente
13
Eres el 'maverick de Java', ¿verdad? Eso haría que su declaración, "Encontré una buena explicación y ejemplo en ...", sea un intento deshonesto de ocultar que está enlazando a su propio sitio web. Su enlace no parece ser importante para la respuesta, de todos modos. La eliminaré para evitar que la respuesta se elimine como spam. Lea las preguntas frecuentes sobre la autopromoción antes de publicar más enlaces a su sitio web. También tenga en cuenta que está bastante bien que coloque el enlace de su sitio web en su perfil.
Andrew Barber
2

Hay una diferencia fundamental entre los dos. En el caso del patrón de diseño Singleton, solo se creará una instancia de una clase por classLoader, mientras que ese no es el caso con Spring singleton, ya que en la última instancia se crea una instancia de bean compartido para el ID dado por contenedor IoC.

Por ejemplo, si tengo una clase con el nombre "SpringTest" y mi archivo XML se parece a esto: -

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

Entonces, ahora en la clase principal, si verifica la referencia de los dos anteriores, devolverá falso como de acuerdo con la documentación de Spring: -

Cuando un bean es un singleton, solo se administrará una instancia compartida del bean, y todas las solicitudes de beans con un ID o ID que coincidan con esa definición de bean darán como resultado que el contenedor Spring devuelva una instancia de bean específica.

Entonces, como en nuestro caso, las clases son las mismas, pero las identificaciones que hemos proporcionado son diferentes, por lo que se crean dos instancias diferentes.

hhhakki
fuente
1

"singleton" en spring usa la instancia de obtención de la fábrica de frijoles, luego la almacena en caché; qué patrón de diseño singleton es estrictamente, la instancia solo se puede recuperar del método get estático, y el objeto nunca se puede instanciar públicamente.

lwpro2
fuente
1

EX: "por contenedor por frijol".

        <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
fuente
<bean class = "com.spring4hibernate4.TestBean"> <constructor-arg name = "i" value = "1"> </constructor-arg> <property name = "name" value = "1-name"> </ propiedad> </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

El frijol spring singleton se describe como "por contenedor por frijol". El alcance Singleton en Spring significa que el mismo objeto en la misma ubicación de memoria se devolverá al mismo ID de bean. Si uno crea varios beans de diferentes ID de la misma clase, el contenedor devolverá diferentes objetos a diferentes ID. Esto es como una asignación de valor clave donde la clave es la identificación del frijol y el valor es el objeto del frijol en un contenedor de primavera. Donde, como patrón Singleton, se asegura que una sola instancia de una clase particular será creada por cargador de clases.

Sibaprasad Sahoo
fuente
1

Todas las respuestas, al menos hasta ahora, se concentran en explicar la diferencia entre el patrón de diseño y el singleton Spring y no abordan su pregunta real: ¿Debería usarse un patrón de diseño Singleton o un bean singleton Spring? ¿Qué es mejor?

Antes de responder, permítame decirle que puede hacer ambas cosas. Puede implementar el bean como un patrón de diseño Singleton y usar Spring para inyectarlo en las clases cliente como un bean singleton de Spring.

Ahora, la respuesta a la pregunta es simple: ¡No use el patrón de diseño Singleton!
Use el bean singleton de Spring implementado como una clase con un constructor público.
¿Por qué? Porque el patrón de diseño Singleton se considera un anti-patrón. Sobre todo porque complica las pruebas. (Y si no usa Spring para inyectarlo, todas las clases que usan el singleton ahora están estrechamente vinculadas a él), y no puede reemplazarlo ni extenderlo. Se puede buscar en Google "Singleton anti-pattern" para obtener más información sobre esto, por ejemplo, Singleton anti-pattern

Usar Spring singleton es el camino a seguir (con un singleton bean implementado NO como un patrón de diseño Singleton, sino con un constructor público) para que el Spring singleton bean pueda probarse fácilmente y las clases que lo usan no estén estrechamente acopladas a él. , sino que Spring inyecta el singleton (como una interfaz) en todos los beans que lo necesitan, y el bean singleton se puede reemplazar en cualquier momento con otra implementación sin afectar a las clases cliente que lo usan.

En o
fuente