Mẫu thiết kế Singleton so với hạt Singleton trong hộp chứa Spring


90

Như chúng ta đã biết, mặc định chúng ta có bean dưới dạng singleton trong Spring container và nếu chúng ta có một ứng dụng web dựa trên Spring framework thì trong trường hợp đó, chúng ta có thực sự cần triển khai mẫu thiết kế Singleton để giữ dữ liệu toàn cục thay vì chỉ tạo một bean thông qua mùa xuân không .

Xin vui lòng chịu đựng nếu tôi không thể giải thích những gì tôi thực sự muốn hỏi.

Câu trả lời:


60

Một hạt đậu đơn vào mùa xuân và mẫu đậu đơn lẻ khá khác nhau. Mẫu Singleton nói rằng một và chỉ một phiên bản của một lớp cụ thể sẽ được tạo cho mỗi bộ nạp lớp.

Phạm vi của một Spring singleton được mô tả là "per container per bean". Đây là phạm vi của định nghĩa bean cho một cá thể đối tượng duy nhất trên vùng chứa Spring IoC. Phạm vi mặc định trong Spring là Singleton.

Mặc dù phạm vi mặc định là singleton, bạn có thể thay đổi phạm vi của bean bằng cách chỉ định thuộc tính phạm vi của <bean ../>phần tử.

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

12
@ user184794: mỗi container cho mỗi bean, nghĩa là chỉ có một trình nạp lớp trong Spring container. Nếu có hai hoặc nhiều hơn hai classloader trong Spring container, thì mỗi trình nạp lớp sẽ có phiên bản riêng. Nó có nghĩa là "mỗi vùng chứa mỗi bộ nạp lớp trên mỗi hạt đậu". Vui lòng làm rõ!!
Dead Programmer

4
tôi nghĩ rằng nó có nghĩa là một vùng chứa Spring sẽ sử dụng một trình nạp lớp duy nhất mà nó sở hữu. những gì bạn làm bên ngoài cơ chế của Spring không liên quan, tức là bạn có thể tạo bộ nạp lớp của riêng mình và tạo bao nhiêu trường hợp của một lớp tùy thích, nhưng nếu bạn đi qua vùng chứa Spring, nó sẽ không tạo nhiều hơn một trường hợp
inor

1
Sau đó, chúng không "hoàn toàn khác" như bạn nói. Sự khác biệt duy nhất là phạm vi - mùa xuân chứa câu classloader
Zack Macomber

30

Phạm vi singleton trong spring có nghĩa là một cá thể duy nhất trong ngữ cảnh Spring ..
Spring container chỉ trả về cùng một cá thể lặp đi lặp lại cho các lần gọi tiếp theo để lấy bean.


Và Spring không bận tâm nếu lớp của bean được mã hóa là singleton hay không, trên thực tế nếu lớp được mã hóa là singleton có hàm tạo là private, Spring sử dụng BeanUtils.instantiateClass (javadoc ở đây ) để đặt hàm tạo thành có thể truy cập và gọi nó.

Ngoài ra, chúng ta có thể sử dụng thuộc tính factory-method trong định nghĩa bean như thế này

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

1
bạn có chắc bạn cần thuộc tính factory-method không? Tôi khá chắc chắn mùa xuân biết làm thế nào để có được một thể hiện ngay cả khi các nhà xây dựng là tin (có thể là cố gắng gọi getInstance)
inor

Một cuộc thảo luận liên quan về cách mùa xuân gọi xây dựng tư nhân ở đây
Xiawei Zhang

21

Hãy lấy ví dụ đơn giản nhất: bạn có một ứng dụng và bạn chỉ sử dụng trình tải lớp mặc định. Bạn có một lớp, vì bất kỳ lý do gì, bạn quyết định rằng nó không nên có nhiều hơn một thể hiện trong ứng dụng. (Hãy nghĩ về một kịch bản trong đó một số người làm việc trên các phần của ứng dụng).

Nếu bạn không sử dụng Spring framework, mẫu Singleton đảm bảo rằng sẽ không có nhiều hơn một phiên bản của một lớp trong ứng dụng của bạn. Đó là bởi vì bạn không thể khởi tạo các thể hiện của lớp bằng cách làm 'mới' vì hàm tạo là riêng tư. Cách duy nhất để lấy một thể hiện của lớp là gọi một số phương thức tĩnh của lớp (thường được gọi là 'getInstance') luôn trả về cùng một thể hiện.

Nói rằng bạn đang sử dụng Spring framework trong ứng dụng của mình, chỉ có nghĩa là ngoài các cách thông thường để lấy một thể hiện của lớp (các phương thức mới hoặc tĩnh trả về một thể hiện của lớp), bạn cũng có thể yêu cầu Spring giúp bạn. một thể hiện của lớp đó và Spring sẽ đảm bảo rằng bất cứ khi nào bạn hỏi nó về một thể hiện của lớp đó, nó sẽ luôn trả về cùng một thể hiện, ngay cả khi bạn không viết lớp bằng cách sử dụng mẫu Singleton. Nói cách khác, ngay cả khi lớp có một hàm tạo công khai, nếu bạn luôn hỏi Spring về một thể hiện của lớp đó, thì Spring sẽ chỉ gọi hàm tạo đó một lần trong suốt vòng đời ứng dụng của bạn.

Thông thường nếu bạn đang sử dụng Spring, bạn chỉ nên sử dụng Spring để tạo các thể hiện và bạn có thể có một hàm tạo công khai cho lớp. Nhưng nếu phương thức khởi tạo của bạn không phải là private, bạn sẽ không thực sự ngăn cản bất kỳ ai tạo các phiên bản mới của lớp trực tiếp, bằng cách bỏ qua Spring.

Nếu bạn thực sự muốn một thể hiện duy nhất của lớp, ngay cả khi bạn sử dụng Spring trong ứng dụng của mình và xác định lớp trong Spring là một lớp đơn, thì cách duy nhất để đảm bảo rằng lớp đó cũng được triển khai bằng cách sử dụng mẫu Singleton. Điều đó đảm bảo rằng sẽ có một phiên bản duy nhất, cho dù mọi người sử dụng Spring để lấy một phiên bản hay bỏ qua Spring.


13

Tôi thấy " mỗi thùng mỗi hạt đậu" khó hiểu . Tôi sẽ nói " một bean cho mỗi id bean trong một vùng chứa ". Hãy xem một ví dụ để hiểu nó. Chúng tôi có một Mẫu lớp đậu. Tôi đã định nghĩa hai bean từ lớp này trong định nghĩa bean, như:

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

Vì vậy, bất cứ khi nào tôi cố gắng lấy bean với id "id1", vùng chứa mùa xuân sẽ tạo một bean, lưu vào bộ nhớ cache và trả về cùng một bean đã từng được tham chiếu với id1. Nếu tôi cố gắng lấy nó bằng id7, một bean khác sẽ được tạo từ lớp Mẫu, nó sẽ được lưu vào bộ nhớ đệm và trả về mỗi khi bạn gọi nó bằng id7.

Điều này khó xảy ra với mẫu Singleton. Trong mẫu Singlton, một đối tượng cho mỗi bộ nạp lớp luôn được tạo. Tuy nhiên trong Spring, việc tạo phạm vi dưới dạng Singleton không hạn chế vùng chứa tạo nhiều cá thể từ lớp đó. Nó chỉ hạn chế việc tạo đối tượng mới cho cùng một ID, trả về đối tượng đã tạo trước đó khi một đối tượng được yêu cầu cho cùng một id . Tài liệu tham khảo


Giải thích tốt. Cảm ơn!
Swapnil

12

Phạm vi Singleton trong Spring có nghĩa là bean này sẽ được khởi tạo chỉ một lần vào Spring. Ngược lại với phạm vi nguyên mẫu (phiên bản mới mỗi lần), phạm vi yêu cầu (một lần cho mỗi yêu cầu), phạm vi phiên (một lần cho mỗi phiên HTTP).

Về mặt kỹ thuật, phạm vi Singleton không liên quan gì đến mẫu thiết kế singleton. Bạn không cần phải triển khai các bean của mình dưới dạng các thẻ đơn để chúng được đưa vào phạm vi singleton.


1
Hãy sửa cho tôi nếu tôi sai vậy theo bạn nếu tôi cần thực hiện bất kỳ đối tượng nào dưới dạng singleton nên không cần thực hiện mô hình singleton. Tạo bean đó bằng cách sử dụng Spring sẽ hoạt động. Bây giờ tôi hơi bối rối với hiểu biết của mình liên quan đến mẫu thiết kế Singleton và phạm vi Singleton trong khuôn khổ Spring.
Peeyush

1
Spring không bắt buộc bạn phải sử dụng mẫu Singleton.
lexicore

2

Các hạt đậu Singleton trong Spring và các lớp dựa trên mẫu thiết kế Singleton khá khác nhau.

Mẫu Singleton đảm bảo rằng một và chỉ một phiên bản của một lớp cụ thể sẽ được tạo cho mỗi bộ nạp lớp trong đó phạm vi của một hạt đậu đơn Spring được mô tả là 'mỗi vùng chứa trên mỗi hạt đậu'. Phạm vi Singleton trong Spring có nghĩa là bean này sẽ được khởi tạo chỉ một lần vào Spring. Spring container chỉ trả về cùng một thể hiện lặp đi lặp lại cho các lần gọi tiếp theo để lấy bean.


13
Bạn là 'java maverick', phải không? Điều đó sẽ khiến tuyên bố của bạn, "Đã tìm thấy một lời giải thích và ví dụ hay tại ...", một nỗ lực không trung thực để che giấu rằng bạn đang liên kết đến trang web của riêng mình. Liên kết của bạn dường như không quan trọng đối với câu trả lời. Tôi đang xóa nó để tránh câu trả lời bị xóa vì spam. Vui lòng đọc Câu hỏi thường gặp về Tự quảng cáo trước khi đăng thêm bất kỳ liên kết nào lên trang web của bạn. cũng lưu ý rằng bạn có thể đưa liên kết trang web vào hồ sơ của mình.
Andrew Barber

2

Có một sự khác biệt rất cơ bản giữa hai điều này. Trong trường hợp mẫu thiết kế Singleton, chỉ một thể hiện của lớp sẽ được tạo trên mỗi classLoader trong khi đó không phải là trường hợp với Spring singleton như trong trường hợp bean được chia sẻ sau đó cho id đã cho trên mỗi vùng chứa IoC được tạo.

Ví dụ: nếu tôi có một lớp với tên "SpringTest" và tệp XML của tôi trông giống như sau: -

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

Vì vậy, bây giờ trong lớp chính nếu bạn kiểm tra tham chiếu của hai tham chiếu trên, nó sẽ trả về false như theo tài liệu Spring: -

Khi một bean là một singleton, chỉ một phiên bản được chia sẻ của bean sẽ được quản lý và tất cả các yêu cầu cho bean có id hoặc id khớp với định nghĩa bean đó sẽ dẫn đến một phiên bản bean cụ thể được trả về bởi vùng chứa Spring

Vì vậy, như trong trường hợp của chúng tôi, các lớp giống nhau nhưng id mà chúng tôi đã cung cấp khác nhau, do đó dẫn đến hai trường hợp khác nhau được tạo.


1

"singleton" trong mùa xuân đang sử dụng thể hiện bean factory get, sau đó lưu vào bộ nhớ cache của nó; mẫu thiết kế singleton nào là đúng, thể hiện chỉ có thể được truy xuất từ ​​phương thức static get và đối tượng không bao giờ có thể được khởi tạo công khai.


1

EX: "per container per bean".

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

1

Spring singleton bean được mô tả là 'mỗi container trên mỗi hạt đậu'. Phạm vi Singleton trong Spring có nghĩa là cùng một đối tượng tại cùng một vị trí bộ nhớ sẽ được trả về cùng id bean. Nếu một người tạo nhiều bean của các id khác nhau của cùng một lớp thì vùng chứa sẽ trả về các đối tượng khác nhau cho các id khác nhau. Điều này giống như một ánh xạ giá trị khóa trong đó khóa là bean id và giá trị là đối tượng bean trong một vùng chứa mùa xuân. Trong đó, mẫu Singleton đảm bảo rằng một và chỉ một phiên bản của một lớp cụ thể sẽ được tạo cho mỗi bộ nạp lớp.


1

Tất cả các câu trả lời, cho đến nay, ít nhất, tập trung vào việc giải thích sự khác biệt giữa mẫu thiết kế và Spring singleton và không giải quyết câu hỏi thực sự của bạn: Nên sử dụng mẫu thiết kế Singleton hay hạt Spring singleton? cái gì tốt hơn?

Trước khi tôi trả lời, hãy để tôi nói rằng bạn có thể làm cả hai. Bạn có thể triển khai bean dưới dạng một mẫu thiết kế Singleton và sử dụng Spring để đưa nó vào các lớp khách hàng dưới dạng một bean singleton của Spring.

Bây giờ, câu trả lời cho câu hỏi rất đơn giản: Không sử dụng mẫu thiết kế Singleton!
Sử dụng bean singleton của Spring được triển khai như một lớp với hàm tạo công khai.
Tại sao? Vì mẫu thiết kế Singleton được coi là mẫu phản. Chủ yếu là vì nó làm phức tạp thử nghiệm. (Và nếu bạn không sử dụng Spring để chèn nó thì tất cả các lớp sử dụng singleton bây giờ đều bị ràng buộc chặt chẽ với nó), và bạn không thể thay thế hoặc mở rộng nó. Người ta có thể google "Singleton anti-pattern" để biết thêm thông tin về điều này, ví dụ như Singleton anti-pattern

Sử dụng Spring singleton là cách để đi (với một singleton bean được triển khai KHÔNG như một mẫu thiết kế Singleton, mà là với một hàm tạo công khai) để Spring singleton bean có thể dễ dàng được kiểm tra và các lớp sử dụng nó không được kết hợp chặt chẽ với nó , nhưng đúng hơn, Spring sẽ đưa singleton (như một giao diện) vào tất cả các bean cần nó, và singleton bean có thể được thay thế bất kỳ lúc nào bằng một triển khai khác mà không ảnh hưởng đến các lớp máy khách sử dụng nó.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.