Wzorzec projektowania singleton vs fasola Singleton w pojemniku Spring

90

Jak wszyscy wiemy, w kontenerze Spring domyślnie mamy ziarna jako singleton i jeśli mamy aplikację internetową opartą na frameworku Spring, to w takim przypadku naprawdę musimy zaimplementować wzorzec projektowy Singleton do przechowywania danych globalnych, a nie tylko do tworzenia fasoli do wiosny .

Proszę o wyrozumiałość, jeśli nie jestem w stanie wyjaśnić, o co właściwie chciałem zapytać.

Peeyush
źródło

Odpowiedzi:

61

Pojedyncza fasola wiosną i pojedyncza fasola są zupełnie inne. Wzorzec singleton mówi, że jedno i tylko jedno wystąpienie określonej klasy zostanie kiedykolwiek utworzone na program ładujący klasy.

Zakres pojedynczego Springa jest opisany jako „na pojemnik na ziarno”. Jest to zakres definicji komponentu bean do pojedynczej instancji obiektu na kontener Spring IoC. Domyślny zakres w Spring to Singleton.

Mimo że domyślnym zakresem jest pojedynczy, można zmienić zakres komponentu bean, określając atrybut scope <bean ../>elementu.

<bean id=".." class=".." scope="prototype" />
user184794
źródło
12
@ user184794: na pojemnik na ziarno, co oznacza, że ​​w pojemniku sprężynowym jest tylko jeden program ładujący klasy. jeśli w pojemniku sprężynowym znajdują się dwa lub więcej modułów ładujących klasy, każdy program ładujący klasy będzie miał własną instancję. Czy to oznacza „na pojemnik na klasę ładującą na ziarna”. uprzejmie wyjaśnij !!
Dead Programmer
4
Myślę, że oznacza to, że kontener Spring będzie używał jednego programu ładującego, którego jest właścicielem. co robisz poza mechanizmem wiosny, nie ma znaczenia, to znaczy można tworzyć własne classloaders i utworzyć dowolną liczbę wystąpień klasy, jak chcesz, ale jeśli przejdziesz przez pojemnik wiosennej, to nie będzie tworzyć więcej niż jedną instancję
Inor
1
Więc nie są „całkiem różne”, jak twierdzisz. Jedyną różnicą jest zasięg - Spring container verses classloader
Zack Macomber
31

Zakres pojedynczy w spring oznacza pojedynczą instancję w kontekście
Spring. Kontener Spring po prostu zwraca tę samą instancję raz po raz dla kolejnych wywołań w celu pobrania komponentu bean.


I spring nie przejmuje się tym, czy klasa fasoli jest zakodowana jako singleton czy nie, w rzeczywistości, jeśli klasa jest zakodowana jako singleton, której konstruktor jest prywatny, Spring używa BeanUtils.instantiateClass (javadoc tutaj ), aby ustawić konstruktor na dostępność i wywołać to.

Alternatywnie, możemy użyć atrybutu metody fabrycznej w takiej definicji fasoli

    <bean id="exampleBean" class="example.Singleton"  factory-method="getInstance"/>
Vasanth
źródło
1
czy na pewno potrzebujesz atrybutu metody fabrycznej? jestem prawie pewien, że Spring wie, jak uzyskać instancję, nawet jeśli konstruktor jest prywatny (prawdopodobnie próbuje wywołać getInstance)
inor
Powiązana dyskusja o tym, jak Spring wywołuje prywatnego konstruktora tutaj
Xiawei Zhang
22

Weźmy najprostszy przykład: masz aplikację i po prostu używasz domyślnego modułu ładującego klasy. Masz klasę, która z jakiegokolwiek powodu decydujesz, że nie powinna mieć więcej niż jednej instancji w aplikacji. (Pomyśl o scenariuszu, w którym kilka osób pracuje nad fragmentami aplikacji).

Jeśli nie korzystasz ze środowiska Spring, wzorzec Singleton zapewnia, że ​​w Twojej aplikacji nie będzie więcej niż jedno wystąpienie klasy. Dzieje się tak, ponieważ nie można utworzyć instancji klasy, wykonując polecenie „new”, ponieważ konstruktor jest prywatny. Jedynym sposobem uzyskania instancji klasy jest wywołanie jakiejś statycznej metody klasy (zwykle nazywanej „getInstance”), która zawsze zwraca tę samą instancję.

Powiedzenie, że używasz frameworka Spring w swojej aplikacji, oznacza po prostu, że oprócz zwykłych sposobów uzyskiwania instancji klasy (nowych lub statycznych metod, które zwracają instancję klasy), możesz również poprosić Springa o uzyskanie instancja tej klasy i Spring zapewni, że za każdym razem, gdy poprosisz ją o instancję tej klasy, zawsze zwróci tę samą instancję, nawet jeśli klasa nie została napisana przy użyciu wzorca Singleton. Innymi słowy, nawet jeśli klasa ma konstruktora publicznego, jeśli zawsze prosisz Springa o instancję tej klasy, Spring wywoła ten konstruktor tylko raz w czasie życia aplikacji.

Zwykle, jeśli używasz Springa, powinieneś używać Springa tylko do tworzenia instancji i możesz mieć publiczny konstruktor dla klasy. Ale jeśli twój konstruktor nie jest prywatny, tak naprawdę nie uniemożliwiasz nikomu bezpośredniego tworzenia nowych instancji klasy, omijając Spring.

Jeśli naprawdę chcesz mieć jedną instancję klasy, nawet jeśli używasz Springa w swojej aplikacji i zdefiniujesz klasę w Spring jako singleton, jedynym sposobem na to jest zaimplementowanie klasy przy użyciu wzorca Singleton. Gwarantuje to, że będzie jedna instancja, niezależnie od tego, czy ludzie używają Springa do uzyskania instancji, czy też pomijają Spring.

inor
źródło
13

Uważam, że „ na pojemnik na ziarno” jest trudne do zrozumienia . Powiedziałbym „ jedno ziarno na identyfikator ziarna w pojemniku ”. Mamy przykład, aby to zrozumieć. Mamy próbkę klasy fasoli. Zdefiniowałem dwie fasole z tej klasy w definicji fasoli, takie jak:

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

Więc kiedy kiedykolwiek spróbuję uzyskać ziarno o identyfikatorze „id1”, pojemnik sprężynowy utworzy jedno ziarno, zapisze je w pamięci podręcznej i zwróci to samo ziarno, o ile kiedykolwiek zostało określone przez id1. Jeśli spróbuję uzyskać to za pomocą id7, inny bean zostanie utworzony z klasy Sample, to samo będzie buforowane i zwracane za każdym razem, gdy odwołasz to z id7.

Jest to mało prawdopodobne w przypadku wzorca Singleton. We wzorcu Singlton zawsze tworzony jest jeden obiekt na program ładujący klasy. Jednak wiosną ustawienie zakresu jako Singleton nie ogranicza kontenera do tworzenia wielu instancji z tej klasy. Po prostu ponownie ogranicza tworzenie nowych obiektów dla tego samego identyfikatora, zwracając wcześniej utworzony obiekt, gdy zażądano obiektu o tym samym identyfikatorze . Odniesienie

Dexter
źródło
Dobrze wyjaśnione. Dzięki!
Swapnil
12

Zakres singletona w Spring oznacza, że ​​instancja tego ziarna zostanie utworzona tylko raz do Spring. W przeciwieństwie do zakresu prototypu (za każdym razem nowa instancja), zakres żądania (raz na żądanie), zakres sesji (raz na sesję HTTP).

Zakres singleton nie ma technicznie nic wspólnego z wzorcem projektowym singleton. Nie musisz implementować swoich bean jako singletonów, aby zostały umieszczone w zakresie singleton.

leksykor
źródło
1
Popraw mnie, jeśli się mylę, więc według ciebie, jeśli muszę zaimplementować dowolny obiekt jako singleton, więc nie ma potrzeby implementowania wzorca singleton. Tworzenie tej fasoli za pomocą Spring będzie działać. Jestem teraz trochę zdezorientowany ze zrozumieniem dotyczącym wzorca Singleton Design i zakresu Singleton we frameworku Spring.
Peeyush,
1
Wiosna nie zmusza Cię do korzystania ze wzoru Singleton.
lexicore,
2

Ziarna singletona na wiosnę i klasy oparte na wzorcu projektowym Singleton są zupełnie inne.

Wzorzec singleton gwarantuje, że jedno i tylko jedno wystąpienie określonej klasy zostanie kiedykolwiek utworzone dla modułu ładującego klasy, przy czym zakres pojedynczego komponentu bean Spring jest opisany jako „na kontener na ziarno”. Zakres singleton w Spring oznacza, że ​​instancja tego ziarna zostanie utworzona tylko raz do Spring. Spring container po prostu zwraca tę samą instancję raz po raz dla kolejnych wywołań w celu pobrania ziarna.

JavaMave
źródło
13
Jesteś „java maverick”, prawda? To sprawiłoby, że Twoje stwierdzenie „Znalazłem dobre wyjaśnienie i przykład w…” byłoby nieuczciwą próbą ukrycia, że ​​łączysz się z własną witryną. Twój link i tak nie wydaje się być ważny dla odpowiedzi. Usuwam go, aby odpowiedź nie została usunięta jako spam. Przeczytaj często zadawane pytania dotyczące autopromocji, zanim opublikujesz więcej linków do swojej witryny. Pamiętaj również, że umieszczenie linku do witryny w swoim profilu jest całkiem w porządku.
Andrew Barber
2

Jest między nimi bardzo fundamentalna różnica. W przypadku wzorca projektowego Singleton, tylko jedna instancja klasy zostanie utworzona na classLoader, podczas gdy tak nie jest w przypadku Spring singleton, ponieważ później tworzona jest jedna współużytkowana instancja fasoli dla danego identyfikatora na kontener IoC.

Na przykład, jeśli mam klasę o nazwie „SpringTest”, a mój plik XML wygląda mniej więcej tak: -

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

Więc teraz w głównej klasie, jeśli sprawdzisz odniesienie do powyższych dwóch, zwróci to fałsz zgodnie z dokumentacją Springa: -

Kiedy ziarno jest singletonem, zarządzana będzie tylko jedna współużytkowana instancja fasoli, a wszystkie żądania dotyczące fasoli o identyfikatorze lub identyfikatorach pasujących do tej definicji będą skutkować zwróceniem tej jednej konkretnej instancji fasoli przez kontener Spring

Tak jak w naszym przypadku, klasy są takie same, ale podane przez nas identyfikatory są różne, co powoduje utworzenie dwóch różnych instancji.

hhhakki
źródło
2

Wszystkie odpowiedzi, przynajmniej do tej pory, koncentrują się na wyjaśnieniu różnicy między wzorcem projektowym a wiosennym singletonem i nie odnoszą się do twojego rzeczywistego pytania: Czy należy użyć wzorca projektowego Singleton, czy też ziarna singleton Spring? co jest lepsze?

Zanim odpowiem, powiem tylko, że możesz zrobić jedno i drugie. Możesz zaimplementować komponent bean jako wzorzec projektowy Singleton i użyć Spring, aby wstrzyknąć go do klas klienta jako pojedynczy bean Spring.

Teraz odpowiedź na pytanie jest prosta: nie używaj wzorca projektowego Singleton!
Użyj pojedynczego komponentu bean Spring zaimplementowanego jako klasa z publicznym konstruktorem.
Czemu? Ponieważ wzorzec projektowy Singleton jest uważany za anty-wzorzec. Głównie dlatego, że komplikuje testowanie. (A jeśli nie użyjesz Springa do wstrzyknięcia, to wszystkie klasy używające singletona są teraz ściśle z nim powiązane) i nie możesz go zastąpić ani rozszerzyć. Można wygooglować "Singleton anti-pattern", aby uzyskać więcej informacji na ten temat, np. Singleton anti-pattern

Korzystanie z singletona Spring jest najlepszym rozwiązaniem (z pojedynczym komponentem bean zaimplementowanym NIE jako wzorzec projektowy Singleton, ale raczej z publicznym konstruktorem), aby można było łatwo przetestować pojedynczy komponent bean Spring, a klasy, które go używają, nie są z nim ściśle powiązane , ale raczej Spring wstrzykuje singleton (jako interfejs) do wszystkich ziaren, które go potrzebują, i można je zastąpić w dowolnym momencie inną implementacją bez wpływu na klasy klienta, które go używają.

inor
źródło
1

"singleton" na wiosnę używa instancji pobierania z fabryki fasoli, a następnie buforuje ją; który wzorzec projektowy singleton jest ściśle, instancja może zostać pobrana tylko ze statycznej metody get, a obiekt nigdy nie może zostać publicznie utworzony.

lwpro2
źródło
1

EX: „na pojemnik na ziarno”.

        <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
źródło
<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

Pojedyncze ziarna wiosenne opisywane są jako „na pojemnik na ziarno”. Zasięg singletonowy w Spring oznacza, że ​​ten sam obiekt w tej samej lokalizacji pamięci zostanie zwrócony do tego samego identyfikatora fasoli. Jeśli tworzy się wiele komponentów bean o różnych identyfikatorach tej samej klasy, to container zwraca różne obiekty do różnych identyfikatorów. Jest to podobne do mapowania wartości klucza, w którym klucz to identyfikator fasoli, a wartość to obiekt fasoli w jednym pojemniku sprężynowym. Gdzie jako wzorzec Singleton zapewnia, że ​​jedno i tylko jedno wystąpienie określonej klasy zostanie kiedykolwiek utworzone na program ładujący klasy.

Sibaprasad Sahoo
źródło