Шаблон проектирования Singleton против бинов Singleton в контейнере Spring

90

Как мы все знаем, у нас есть beans как singleton по умолчанию в контейнере Spring, и если у нас есть веб-приложение на основе Spring framework, тогда в этом случае нам действительно нужно реализовать шаблон проектирования Singleton для хранения глобальных данных, а не просто создавать bean через spring .

Пожалуйста, потерпите меня, если я не могу объяснить, о чем на самом деле хотел спросить.

Peeyush
источник

Ответы:

61

Одноэлементный компонент в Spring и одноэлементный шаблон сильно отличаются. Шаблон синглтона говорит, что один и только один экземпляр определенного класса будет когда-либо создан для загрузчика классов.

Объем синглтона Spring описывается как «на контейнер на компонент». Это область определения bean-компонента для одного экземпляра объекта на контейнер Spring IoC. Область действия по умолчанию в Spring - Singleton.

Несмотря на то, что область видимости по умолчанию - одноэлементная, вы можете изменить область действия bean, указав атрибут scope <bean ../>элемента.

<bean id=".." class=".." scope="prototype" />
user184794
источник
12
@ user184794: для каждого контейнера на bean-компонент, что означает, что в контейнере spring есть только один загрузчик классов. если в контейнере spring есть два или более загрузчика классов, то каждый загрузчик классов будет иметь собственный экземпляр. Означает ли это «на контейнер, на загрузчик классов, на компонент». добро уточнить !!
Мертвый программист
4
Я думаю, это означает, что контейнер Spring будет использовать единственный загрузчик классов, которым он владеет. то, что вы делаете вне механизма Spring, не имеет значения, то есть вы можете создавать свои собственные загрузчики классов и создавать столько экземпляров класса, сколько хотите, но если вы пройдете через контейнер Spring, он не создаст более одного экземпляра
inor
1
Тогда они не «совсем другие», как вы утверждаете. Единственное различие заключается в объеме - загрузчик классов стихов из контейнера Spring
Зак Макомбер
31

Одноэлементная область видимости в Spring означает единственный экземпляр в контексте
Spring. Контейнер Spring просто возвращает один и тот же экземпляр снова и снова для последующих вызовов для получения bean-компонента.


И Spring не беспокоит, закодирован ли класс bean-компонента как singleton или нет, на самом деле, если класс закодирован как singleton, конструктор которого как закрытый, Spring использует BeanUtils.instantiateClass ( здесь javadoc ), чтобы установить доступный конструктор и вызвать Это.

В качестве альтернативы мы можем использовать атрибут factory-method в определении bean-компонента, например

    <bean id="exampleBean" class="example.Singleton"  factory-method="getInstance"/>
Васант
источник
1
вы уверены, что вам нужен атрибут factory-method? я почти уверен, что Spring знает, как получить экземпляр, даже если конструктор является частным (вероятно, пытается вызвать getInstance)
inor
Связанное обсуждение того, как Spring вызывает частный конструктор здесь
Xiawei Zhang
22

Возьмем простейший пример: у вас есть приложение, и вы просто используете загрузчик классов по умолчанию. У вас есть класс, который по какой-то причине вы решили, что он не должен иметь более одного экземпляра в приложении. (Подумайте о сценарии, когда несколько человек работают над частями приложения).

Если вы не используете платформу Spring, шаблон Singleton гарантирует, что в вашем приложении не будет более одного экземпляра класса. Это потому, что вы не можете создать экземпляры класса, выполняя 'new', потому что конструктор является частным. Единственный способ получить экземпляр класса - вызвать некоторый статический метод класса (обычно называемый getInstance), который всегда возвращает один и тот же экземпляр.

Сказать, что вы используете платформу Spring в своем приложении, просто означает, что в дополнение к обычным способам получения экземпляра класса (новые или статические методы, возвращающие экземпляр класса), вы также можете попросить Spring предоставить вам экземпляр этого класса, и Spring гарантирует, что всякий раз, когда вы запрашиваете экземпляр этого класса, он всегда будет возвращать один и тот же экземпляр, даже если вы не писали класс с использованием шаблона Singleton. Другими словами, даже если у класса есть общедоступный конструктор, если вы всегда запрашиваете у Spring экземпляр этого класса, Spring вызовет этот конструктор только один раз в течение жизни вашего приложения.

Обычно, если вы используете Spring, вы должны использовать Spring только для создания экземпляров, и у вас может быть общедоступный конструктор для класса. Но если ваш конструктор не является частным, вы не мешаете кому-либо создавать новые экземпляры класса напрямую, минуя Spring.

Если вам действительно нужен единственный экземпляр класса, даже если вы используете Spring в своем приложении и определяете класс в Spring как singleton, единственный способ гарантировать, что это также реализует класс с использованием шаблона Singleton. Это гарантирует, что будет один экземпляр, независимо от того, используют ли люди Spring для получения экземпляра или обходят Spring.

инор
источник
13

Я считаю, что «из расчета на контейнер на зерно» трудно понять . Я бы сказал « один компонент на каждый идентификатор компонента в контейнере ». Давайте разберемся на примере, чтобы понять это. У нас есть бин-класс Sample. Я определил два компонента из этого класса в определении компонента, например:

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

Поэтому, когда я когда-либо пытаюсь получить bean-компонент с идентификатором «id1», контейнер Spring создает один bean-компонент, кэширует его и возвращает тот же bean-компонент, где когда-либо упоминался с id1. Если я попытаюсь получить его с помощью id7, другой bean будет создан из класса Sample, он будет кэшироваться и возвращаться каждый раз, когда вы ссылаетесь на него с помощью id7.

Это маловероятно с шаблоном Singleton. В шаблоне Singlton всегда создается один объект для каждого загрузчика классов. Однако в Spring создание области как Singleton не ограничивает контейнер от создания множества экземпляров из этого класса. Он просто снова ограничивает создание нового объекта для того же идентификатора, возвращая ранее созданный объект, когда объект запрашивается для того же идентификатора . Справка

Декстер
источник
Хорошо объяснено. Благодарность!
Swapnil
12

Одиночная область видимости в Spring означает, что этот компонент будет создан Spring только один раз. В отличие от области прототипа (новый экземпляр каждый раз), области запроса (один раз на запрос), области сеанса (один раз на сеанс HTTP).

Область видимости синглтона технически не имеет ничего общего с шаблоном проектирования синглтона. Вам не нужно реализовывать bean-компоненты как синглтоны, чтобы их можно было поместить в синглтон-область.

лексикор
источник
1
Поправьте меня, если я ошибаюсь, по вашему мнению, если мне нужно реализовать какой-либо объект как одноэлементный, поэтому нет необходимости реализовывать одноэлементный шаблон. Создание этого bean-компонента с использованием Spring будет работать. Я немного сбит с толку своим пониманием, связанным с шаблоном проектирования Singleton и областью действия Singleton в среде Spring.
Peeyush
1
Spring не заставляет вас использовать шаблон Singleton.
lexicore
2

Компоненты Singleton в Spring и классы, основанные на шаблоне проектирования Singleton, сильно отличаются.

Шаблон Singleton гарантирует, что один и только один экземпляр определенного класса будет когда-либо создан для каждого загрузчика классов, тогда как область действия singleton bean-компонента Spring описывается как «на контейнер для каждого bean-компонента». Одиночная область видимости в Spring означает, что этот компонент будет создан Spring только один раз. Контейнер Spring просто возвращает один и тот же экземпляр снова и снова для последующих вызовов для получения bean-компонента.

JavaMave
источник
13
Вы же "сторонник Java", верно? Это сделало бы ваше заявление «Нашел хорошее объяснение и пример в ...» нечестной попыткой скрыть, что вы ссылаетесь на свой собственный веб-сайт. В любом случае ваша ссылка не кажется важной для ответа. Я удаляю его, чтобы ответ не был удален как спам. Пожалуйста, прочтите FAQ по саморекламе, прежде чем размещать какие-либо ссылки на свой сайт. также обратите внимание, что вы можете разместить ссылку на свой веб-сайт в своем профиле.
Эндрю Барбер
2

Между ними есть очень фундаментальная разница. В случае шаблона проектирования Singleton для каждого classLoader будет создан только один экземпляр класса, в то время как в случае Spring singleton это не так, поскольку в последнем случае создается один экземпляр общего компонента для данного идентификатора для каждого контейнера IoC.

Например, если у меня есть класс с именем «SpringTest» и мой XML-файл выглядит примерно так: -

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

Итак, теперь в основном классе, если вы проверите ссылку на два вышеупомянутых, он вернет false, как указано в документации Spring: -

Когда bean-компонент является синглтоном, будет управляться только один общий экземпляр bean-компонента, и все запросы для bean-компонентов с идентификатором или идентификаторами, соответствующими этому определению bean-компонента, приведут к тому, что этот конкретный экземпляр bean-компонента будет возвращен контейнером Spring.

Итак, как и в нашем случае, классы одинаковы, но предоставленные нами идентификаторы отличаются, что приводит к созданию двух разных экземпляров.

хххакки
источник
2

Все ответы, по крайней мере до сих пор, сосредоточены на объяснении разницы между шаблоном проектирования и синглтоном Spring и не касаются вашего фактического вопроса: следует ли использовать шаблон проектирования синглтона или одноэлементный компонент Spring? что лучше?

Прежде чем я отвечу, позвольте мне заявить, что вы можете сделать и то, и другое. Вы можете реализовать bean-компонент как шаблон проектирования Singleton и использовать Spring для внедрения его в клиентские классы как singleton-bean-компонент Spring.

Теперь ответ на вопрос прост: не используйте шаблон проектирования Singleton!
Используйте одноэлементный bean-компонент Spring, реализованный как класс с открытым конструктором.
Зачем? Поскольку шаблон проектирования Singleton считается анти-шаблоном. В основном потому, что это усложняет тестирование. (И если вы не используете Spring для его внедрения, тогда все классы, использующие синглтон, теперь жестко привязаны к нему), и вы не можете заменить или расширить его. Можно погуглить "Singleton anti-pattern", чтобы получить больше информации об этом, например, Singleton anti-pattern.

Использование синглтона Spring - это правильный путь (с одиночным bean-компонентом, реализованным НЕ как шаблон проектирования Singleton, а с общедоступным конструктором), чтобы синглтон-компонент Spring можно было легко протестировать, а классы, которые его используют, не были тесно связаны с ним , а скорее Spring внедряет синглтон (как интерфейс) во все бины, которые в нем нуждаются, и его можно в любой момент заменить другой реализацией, не затрагивая клиентские классы, которые его используют.

инор
источник
1

"singleton" весной использует экземпляр фабрики bean-компонентов, а затем кеширует его; какой шаблон проектирования синглтона является строгим, экземпляр может быть получен только из статического метода get, а объект никогда не может быть создан публично.

lwpro2
источник
1

EX: «в контейнере на зерно».

        <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"> </ property> </bean> <bean class = "com.spring4hibernate4.TestBean"> <constructor-arg name = "i" value = "10"> </constructor-arg> <property name = "name" value = "10 -name "> </property> </bean> </beans>
Харипрасад
1

Одиночный компонент Spring описывается как «на контейнер на компонент». Одиночная область видимости в Spring означает, что тот же объект в том же месте памяти будет возвращен с тем же идентификатором bean-компонента. Если создать несколько bean-компонентов с разными идентификаторами одного и того же класса, контейнер будет возвращать разные объекты с разными идентификаторами. Это похоже на отображение значения ключа, где ключ - это идентификатор компонента, а значение - объект компонента в одном контейнере Spring. Шаблон Where as Singleton гарантирует, что один и только один экземпляр определенного класса когда-либо будет создан для каждого загрузчика классов.

Сибапрасад Саху
источник