Padrão de design singleton vs grãos singleton no recipiente Spring


90

Como todos sabemos, temos beans como singleton por padrão no recipiente Spring e se tivermos um aplicativo da web baseado no framework Spring, então, nesse caso, realmente precisamos implementar o padrão de design Singleton para manter dados globais em vez de apenas criar um bean durante a primavera .

Por favor, tenha paciência comigo se eu não sou capaz de explicar o que realmente quis perguntar.

Respostas:


60

Um único feijão na primavera e o padrão singleton são bastante diferentes. O padrão Singleton diz que uma e apenas uma instância de uma classe específica será criada por carregador de classe.

O escopo de um singleton Spring é descrito como "por contêiner por feijão". É o escopo da definição de bean para uma única instância de objeto por contêiner Spring IoC. O escopo padrão no Spring é Singleton.

Mesmo que o escopo padrão seja singleton, você pode alterar o escopo do bean especificando o atributo de escopo do <bean ../>elemento.

<bean id=".." class=".." scope="prototype" />

12
@ user184794: por contêiner por bean, o que significa que há apenas um classloader no spring container. se houver dois ou mais classloader no spring container, cada classloader terá sua própria instância. Isso significa "por contêiner por carregador de classe por bean". esclareça gentilmente !!
Dead Programmer

4
Acho que isso significa que um contêiner Spring usará um único carregador de classe de sua propriedade. o que você faz fora do mecanismo do Spring não é relevante, ou seja, você pode criar seus próprios carregadores de classe e criar quantas instâncias de uma classe desejar, mas se você passar pelo contêiner do Spring, ele não criará mais de uma instância
ou

1
Então eles não são "muito diferentes", como você afirma. A única diferença é o escopo - Spring container versus classloader
Zack Macomber

30

Escopo de singleton em spring significa instância única em um contexto Spring.
O container Spring simplesmente retorna a mesma instância repetidamente para chamadas subsequentes para obter o bean.


E o spring não se preocupa se a classe do bean é codificada como singleton ou não, na verdade, se a classe é codificada como singleton cujo construtor é privado, Spring usa BeanUtils.instantiateClass (javadoc aqui ) para definir o construtor para acessível e invocar isto.

Alternativamente, podemos usar um atributo de método de fábrica na definição de bean como este

    <bean id="exampleBean" class="example.Singleton"  factory-method="getInstance"/>

1
tem certeza de que precisa do atributo de método de fábrica? tenho certeza que o Spring sabe como obter uma instância mesmo se o construtor for privado (provavelmente tenta chamar getInstance)
ou

Uma discussão relacionada sobre como o Spring invoca o construtor privado aqui
Xiawei Zhang,

21

Vamos pegar o exemplo mais simples: você tem um aplicativo e apenas usa o carregador de classe padrão. Você tem uma classe que, por qualquer motivo, decide que não deve haver mais de uma instância no aplicativo. (Pense em um cenário onde várias pessoas trabalham em partes do aplicativo).

Se você não estiver usando a estrutura Spring, o padrão Singleton garante que não haverá mais de uma instância de uma classe em seu aplicativo. Isso ocorre porque você não pode instanciar instâncias da classe fazendo 'novo' porque o construtor é privado. A única maneira de obter uma instância da classe é chamar algum método estático da classe (geralmente chamado de 'getInstance') que sempre retorna a mesma instância.

Dizer que você está usando o framework Spring em seu aplicativo, significa apenas que além das formas regulares de obter uma instância da classe (métodos novos ou estáticos que retornam uma instância da classe), você também pode pedir ao Spring para obter uma instância dessa classe e o Spring garantirão que sempre que você solicitar uma instância dessa classe, ela sempre retornará a mesma instância, mesmo que você não tenha escrito a classe usando o padrão Singleton. Em outras palavras, mesmo se a classe tiver um construtor público, se você sempre pedir ao Spring uma instância dessa classe, o Spring só chamará esse construtor uma vez durante a vida de seu aplicativo.

Normalmente, se você estiver usando o Spring, deve usar o Spring apenas para criar instâncias e pode ter um construtor público para a classe. Mas se seu construtor não for privado, você não está realmente impedindo ninguém de criar novas instâncias da classe diretamente, ignorando o Spring.

Se você realmente deseja uma única instância da classe, mesmo se usar Spring em seu aplicativo e definir a classe em Spring como um singleton, a única maneira de garantir isso também é implementar a classe usando o padrão Singleton. Isso garante que haverá uma única instância, quer as pessoas usem Spring para obter uma instância ou contornar o Spring.


13

Acho " por contêiner por feijão" difícil de apreender . Eu diria " um feijão por id de feijão em um contêiner ". Vamos dar um exemplo para entender isso. Temos uma amostra de classe de feijão. Eu defini dois beans desta classe na definição 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>

Portanto, sempre que tento obter o bean com id "id1", o container spring irá criar um bean, armazená-lo em cache e retornar o mesmo bean sempre que referenciado com id1. Se eu tentar obtê-lo com id7, outro bean será criado a partir da classe Sample, o mesmo será armazenado em cache e retornado cada vez que você referenciou isso com id7.

Isso é improvável com o padrão Singleton. No padrão Singlton, um objeto por carregador de classe é criado sempre. No entanto, no Spring, definir o escopo como Singleton não restringe o contêiner de criar muitas instâncias dessa classe. Ele apenas restringe a criação de novos objetos para o mesmo ID novamente, retornando o objeto criado anteriormente quando um objeto é solicitado para o mesmo ID . Referência


Bem explicado. Obrigado!
Swapnil

12

O escopo do singleton no Spring significa que esse bean será instanciado apenas uma vez pelo Spring. Em contraste com o escopo do protótipo (nova instância a cada vez), escopo da solicitação (uma vez por solicitação), escopo da sessão (uma vez por sessão HTTP).

O escopo do singleton não tem nada a ver tecnicamente com o padrão de design do singleton. Você não precisa implementar seus beans como singletons para que eles sejam colocados no escopo singleton.


1
Corrija-me se eu estiver errado, de acordo com você, se eu precisar implementar qualquer objeto como singleton, de forma que não haja necessidade de implementar o padrão singleton. A criação desse bean usando Spring funcionará. Estou um pouco confuso agora com meu entendimento relacionado ao padrão Singleton Design e ao escopo Singleton no framework Spring.
Peeyush

1
Spring não força você a usar o padrão Singleton.
Lexicore

2

Os grãos singleton no Spring e as classes baseadas no padrão de design Singleton são bastante diferentes.

O padrão Singleton garante que uma e apenas uma instância de uma classe específica será criada por carregador de classe, onde o escopo de um bean singleton Spring é descrito como 'por contêiner por bean'. O escopo do singleton no Spring significa que esse bean será instanciado apenas uma vez pelo Spring. O Spring container simplesmente retorna a mesma instância repetidamente para chamadas subsequentes para obter o bean.


13
Você é o 'maverick java', certo? Isso tornaria sua declaração, "Encontrei uma boa explicação e exemplo em ...", uma tentativa desonesta de esconder que você está criando um link para seu próprio site. De qualquer maneira, seu link não parece ser importante para a resposta. Estou removendo, para evitar que a resposta seja excluída como spam. Leia as Perguntas frequentes sobre autopromoção antes de postar mais links em seu site. Observe também que não há problema em colocar o link do seu site no perfil.
Andrew Barber

2

Existe uma diferença fundamental entre os dois. No caso do padrão de design Singleton, apenas uma instância de uma classe será criada por classLoader, enquanto esse não é o caso com o singleton do Spring, pois no último uma instância de bean compartilhado para o id fornecido por contêiner IoC é criada.

Por exemplo, se eu tiver uma classe com o nome "SpringTest" e meu arquivo XML for algo assim: -

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

Portanto, agora na classe principal, se você verificar a referência das duas anteriores, ela retornará falso de acordo com a documentação do Spring: -

Quando um bean é um singleton, apenas uma instância compartilhada do bean será gerenciada, e todos os pedidos de beans com um id ou ids correspondentes a essa definição de bean resultarão em uma instância de bean específica sendo retornada pelo contêiner Spring

Assim, como em nosso caso, as classes são as mesmas, mas os ids que fornecemos são diferentes, resultando na criação de duas instâncias diferentes.


1

"singleton" na primavera é usar a instância get do bean factory e, em seguida, armazená-la em cache; cujo padrão de design singleton é estritamente, a instância só pode ser recuperada do método get estático e o objeto nunca pode ser instanciado publicamente.


1

EX: "por recipiente por feijão".

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

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

O feijão singleton da primavera é descrito como 'por contêiner por feijão'. O escopo do singleton no Spring significa que o mesmo objeto no mesmo local de memória será retornado ao mesmo ID do bean. Se alguém criar vários beans de diferentes ids da mesma classe, o container retornará diferentes objetos para diferentes ids. Isso é como um mapeamento de valor-chave, onde a chave é o id do bean e o valor é o objeto do bean em um container spring. Onde o padrão Singleton garante que uma e apenas uma instância de uma determinada classe será criada por carregador de classe.


1

Todas as respostas, pelo menos até agora, concentram-se em explicar a diferença entre o padrão de projeto e o singleton Spring e não respondem à sua pergunta real: deve ser usado um padrão de projeto Singleton ou um bean singleton Spring? o que é melhor?

Antes de responder, deixe-me apenas afirmar que você pode fazer as duas coisas. Você pode implementar o bean como um padrão de design Singleton e usar Spring para injetá-lo nas classes de cliente como um singleton bean Spring.

Agora, a resposta à pergunta é simples: não use o padrão de design Singleton!
Use o singleton bean do Spring implementado como uma classe com construtor público.
Por quê? Porque o padrão de design Singleton é considerado um antipadrão. Principalmente porque complica os testes. (E se você não usar o Spring para injetá-lo, todas as classes que usam o singleton agora estão fortemente ligadas a ele) e você não pode substituí-lo ou estendê-lo. Pode-se pesquisar no Google "Anti-padrão Singleton" para obter mais informações sobre isso, por exemplo, anti-padrão Singleton

Usar o singleton do Spring é o caminho a percorrer (com o bean singleton implementado NÃO como um padrão de design Singleton, mas sim com um construtor público) para que o bean singleton do Spring possa ser facilmente testado e as classes que o usam não sejam fortemente acopladas a ele , mas em vez disso, o Spring injeta o singleton (como uma interface) em todos os beans que precisam dele, e o bean singleton pode ser substituído a qualquer momento por outra implementação sem afetar as classes do cliente que o utilizam.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.