รูปแบบการออกแบบ Singleton เทียบกับถั่ว Singleton ใน Spring container


91

อย่างที่เราทราบกันดีว่าเรามีถั่วเป็นซิงเกิลตันตามค่าเริ่มต้นใน Spring container และหากเรามีเว็บแอปพลิเคชันที่ใช้ Spring framework แล้วในกรณีนี้เราจำเป็นต้องใช้รูปแบบการออกแบบ Singleton เพื่อเก็บข้อมูลทั่วโลกแทนที่จะสร้าง bean ผ่านฤดูใบไม้ผลิ .

โปรดอดทนกับฉันหากฉันไม่สามารถอธิบายสิ่งที่ฉันตั้งใจจะถามได้

คำตอบ:


62

ถั่วซิงเกิลตันในฤดูใบไม้ผลิและรูปแบบซิงเกิลตันนั้นแตกต่างกันมาก รูปแบบ Singleton กล่าวว่าจะมีการสร้างอินสแตนซ์ของคลาสเฉพาะหนึ่งชุดต่อ classloader

ขอบเขตของ Spring singleton อธิบายว่า "ต่อคอนเทนเนอร์ต่อถั่ว" เป็นขอบเขตของนิยาม bean สำหรับอินสแตนซ์อ็อบเจ็กต์เดียวต่อคอนเทนเนอร์ Spring IoC ขอบเขตเริ่มต้นใน Spring คือ Singleton

แม้ว่าขอบเขตเริ่มต้นจะเป็น singleleton แต่คุณสามารถเปลี่ยนขอบเขตของ bean ได้โดยระบุแอตทริบิวต์ขอบเขตของ<bean ../>องค์ประกอบ

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

12
@ user184794: ต่อคอนเทนเนอร์ต่อถั่วหมายความว่ามี classloader เพียงตัวเดียวใน spring container หากมี classloader สองตัวหรือมากกว่าใน spring container แต่ละ classloader จะมีอินสแตนซ์ของตัวเอง หมายความว่า "per container per classloader per bean" หรือเปล่า กรุณาชี้แจง !!
Dead Programmer

4
ฉันคิดว่ามันหมายความว่า Spring container จะใช้ classloader ตัวเดียวที่มันเป็นเจ้าของ สิ่งที่คุณทำนอกกลไกของ Spring นั้นไม่เกี่ยวข้องนั่นคือคุณสามารถสร้าง classloaders ของคุณเองและสร้างอินสแตนซ์ของคลาสได้มากเท่าที่คุณต้องการ แต่ถ้าคุณผ่าน Spring container มันจะไม่สร้างมากกว่าหนึ่งอินสแตนซ์
inor

1
จากนั้นก็ไม่ได้ "แตกต่างกันมาก" อย่างที่คุณระบุ ข้อแตกต่างเพียงอย่างเดียวคือขอบเขต - คลาสตัวโหลดสปริงคอนเทนเนอร์
Zack Macomber

31

ขอบเขต Singleton ในฤดูใบไม้ผลิหมายถึงอินสแตนซ์เดี่ยวในบริบท Spring ..
Spring container เพียงส่งคืนอินสแตนซ์เดียวกันซ้ำแล้วซ้ำอีกสำหรับการเรียกที่ตามมาเพื่อรับ bean


และฤดูใบไม้ผลิไม่ต้องกังวลว่าคลาสของถั่วจะถูกเข้ารหัสเป็นซิงเกิลตันหรือไม่ในความเป็นจริงหากคลาสถูกเข้ารหัสเป็นซิงเกิลตันที่มีคอนสตรัคเตอร์เป็นส่วนตัว Spring ใช้ BeanUtils.instantiateClass (javadoc ที่นี่ ) เพื่อตั้งค่าคอนสตรัคเตอร์ให้เข้าถึงได้และเรียกใช้ มัน.

หรือเราสามารถใช้แอตทริบิวต์ factory-method ในการกำหนด bean เช่นนี้

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

1
คุณแน่ใจหรือไม่ว่าต้องการแอตทริบิวต์วิธีการจากโรงงาน ผมค่อนข้างมั่นใจว่าฤดูใบไม้ผลิรู้วิธีที่จะได้รับเช่นแม้ว่าสร้างความเป็นส่วนตัว (อาจจะพยายามเรียก getInstance)
Inor

การอภิปรายที่เกี่ยวข้องเกี่ยวกับวิธีที่ Spring เรียกใช้ผู้สร้างส่วนตัวที่นี่
Xiawei Zhang

22

ลองดูตัวอย่างที่ง่ายที่สุด: คุณมีแอปพลิเคชันและคุณใช้ classloader เริ่มต้น คุณมีคลาสที่ไม่ว่าด้วยเหตุผลใดคุณตัดสินใจว่าไม่ควรมีมากกว่าหนึ่งอินสแตนซ์ในแอปพลิเคชัน (ลองนึกถึงสถานการณ์ที่มีคนหลายคนทำงานกับชิ้นส่วนของแอปพลิเคชัน)

หากคุณไม่ได้ใช้ Spring framework รูปแบบ Singleton ช่วยให้มั่นใจได้ว่าจะไม่มีอินสแตนซ์ของคลาสมากกว่าหนึ่งอินสแตนซ์ในแอปพลิเคชันของคุณ นั่นเป็นเพราะคุณไม่สามารถสร้างอินสแตนซ์อินสแตนซ์ของคลาสด้วยการทำ 'ใหม่' ได้เนื่องจากตัวสร้างเป็นแบบส่วนตัว วิธีเดียวที่จะได้อินสแตนซ์ของคลาสคือการเรียกเมธอดแบบคงที่ของคลาส (โดยปกติเรียกว่า 'getInstance') ซึ่งจะส่งคืนอินสแตนซ์เดียวกันเสมอ

การบอกว่าคุณกำลังใช้ Spring framework ในแอปพลิเคชันของคุณนั่นหมายความว่านอกเหนือจากวิธีการรับอินสแตนซ์ของคลาสปกติแล้ว (วิธีการใหม่หรือแบบคงที่ที่ส่งคืนอินสแตนซ์ของคลาส) คุณยังสามารถขอให้ Spring ช่วยคุณได้ อินสแตนซ์ของคลาสนั้นและ Spring จะช่วยให้มั่นใจได้ว่าเมื่อใดก็ตามที่คุณถามอินสแตนซ์ของคลาสนั้นมันจะส่งคืนอินสแตนซ์เดียวกันเสมอแม้ว่าคุณจะไม่ได้เขียนคลาสโดยใช้รูปแบบซิงเกิลตันก็ตาม กล่าวอีกนัยหนึ่งแม้ว่าชั้นเรียนจะมีตัวสร้างสาธารณะ แต่ถ้าคุณขอให้ Spring เป็นตัวอย่างของคลาสนั้นเสมอ Spring จะเรียกตัวสร้างนั้นเพียงครั้งเดียวในช่วงชีวิตของแอปพลิเคชันของคุณ

โดยปกติถ้าคุณใช้ Spring คุณควรใช้ Spring เพื่อสร้างอินสแตนซ์เท่านั้นและคุณสามารถมีตัวสร้างสาธารณะสำหรับคลาสได้ แต่ถ้าตัวสร้างของคุณไม่เป็นส่วนตัวคุณก็ไม่ได้ป้องกันไม่ให้ใครสร้างอินสแตนซ์ใหม่ของคลาสโดยตรงโดยข้าม Spring

หากคุณต้องการอินสแตนซ์เดียวของคลาสแม้ว่าคุณจะใช้ Spring ในแอปพลิเคชันของคุณและกำหนดคลาสใน Spring เป็นซิงเกิลตันวิธีเดียวที่จะทำให้แน่ใจได้ว่าจะใช้คลาสโดยใช้รูปแบบ Singleton ด้วย นั่นทำให้มั่นใจได้ว่าจะมีอินสแตนซ์เดียวไม่ว่าผู้คนจะใช้ Spring เพื่อรับอินสแตนซ์หรือบายพาส Spring


16

ฉันพบ " ต่อภาชนะต่อถั่ว" ยากที่จะจับกุม ฉันจะพูดว่า " one bean per bean id in a container " ให้มีตัวอย่างเพื่อทำความเข้าใจ เรามีตัวอย่างคลาสถั่ว ฉันได้กำหนดถั่วสองชนิดจากคลาสนี้ในความหมายของถั่วเช่น:

<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 ที่มี id "id1" คอนเทนเนอร์สปริงจะสร้างถั่วขึ้นมาหนึ่งแคชและส่งคืน bean เดิมที่เคยอ้างถึงด้วย id1 ถ้าฉันพยายามใช้ id7 ถั่วอื่นจะถูกสร้างขึ้นจากคลาสตัวอย่างเดียวกันจะถูกแคชและส่งคืนทุกครั้งที่คุณอ้างถึงสิ่งนั้นด้วย id7

สิ่งนี้ไม่น่าเกิดขึ้นกับรูปแบบ Singleton ในรูปแบบ Singlton หนึ่งอ็อบเจ็กต์ต่อคลาส loader ถูกสร้างขึ้นเสมอ อย่างไรก็ตามใน Spring การสร้างขอบเขตเป็น Singleton ไม่ได้ จำกัด คอนเทนเนอร์จากการสร้างอินสแตนซ์จำนวนมากจากคลาสนั้น มันก็ จำกัด การสร้างวัตถุใหม่สำหรับ ID เดียวกันอีกครั้งกลับวัตถุที่สร้างขึ้นก่อนหน้านี้เมื่อวัตถุมีการร้องขอสำหรับ ID ข้อมูลอ้างอิง


1
อธิบายได้ดี ขอบคุณ!
Swapnil

12

ขอบเขต Singleton ใน Spring หมายความว่าถั่วนี้จะถูกสร้างอินสแตนซ์เพียงครั้งเดียวใน Spring ตรงกันข้ามกับขอบเขตต้นแบบ (อินสแตนซ์ใหม่ทุกครั้ง) ขอบเขตคำขอ (หนึ่งครั้งต่อคำขอ) ขอบเขตเซสชัน (หนึ่งครั้งต่อเซสชัน HTTP)

ขอบเขต Singleton ไม่เกี่ยวข้องกับรูปแบบการออกแบบซิงเกิลตันในทางเทคนิค คุณไม่จำเป็นต้องใช้ถั่วของคุณเป็นซิงเกิลตันเพื่อให้พวกมันอยู่ในขอบเขตซิงเกิลตัน


1
แก้ไขฉันถ้าฉันผิดตามคุณถ้าฉันต้องการใช้วัตถุใด ๆ เป็นซิงเกิลตันดังนั้นจึงไม่จำเป็นต้องใช้รูปแบบซิงเกิลตัน การสร้างถั่วโดยใช้ Spring จะได้ผล ตอนนี้ฉันสับสนเล็กน้อยกับความเข้าใจของฉันที่เกี่ยวข้องกับรูปแบบการออกแบบ Singleton และขอบเขต Singleton ใน Spring framework
Peeyush

1
Spring ไม่ได้บังคับให้คุณใช้รูปแบบ Singleton
พจนานุกรม

3

มีความแตกต่างพื้นฐานระหว่างทั้งสองอย่างมาก ในกรณีของรูปแบบการออกแบบ Singleton จะมีการสร้างเพียงหนึ่งอินสแตนซ์ของคลาสต่อ classLoader ในขณะที่ไม่เป็นเช่นนั้นกับ Spring singleton เช่นเดียวกับอินสแตนซ์ bean ที่แบ่งใช้ในภายหลังสำหรับ id ที่กำหนดต่อคอนเทนเนอร์ 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>

ดังนั้นในคลาสหลักหากคุณจะตรวจสอบการอ้างอิงของสองข้อข้างต้นมันจะส่งคืนเท็จตามเอกสาร Spring: -

เมื่อ bean เป็นซิงเกิลตันจะมีการจัดการอินสแตนซ์ของ bean ที่ใช้ร่วมกันเพียงอินสแตนซ์เดียวและคำขอทั้งหมดสำหรับ bean ที่มี id หรือ ids ที่ตรงกับคำนิยาม bean นั้นจะส่งผลให้อินสแตนซ์ bean หนึ่งรายการถูกส่งคืนโดยคอนเทนเนอร์ Spring

ดังนั้นในกรณีของเราคลาสจะเหมือนกัน แต่ id ที่เราระบุไว้นั้นแตกต่างกันจึงส่งผลให้มีการสร้างอินสแตนซ์สองอินสแตนซ์


2

ถั่วซิงเกิลตันในฤดูใบไม้ผลิและคลาสตามรูปแบบการออกแบบ Singleton นั้นแตกต่างกันมาก

รูปแบบ Singleton ช่วยให้มั่นใจได้ว่าจะมีการสร้างอินสแตนซ์ของคลาสที่เฉพาะเจาะจงเพียงหนึ่งเดียวต่อคลาสโหลดเดอร์โดยที่ขอบเขตของ Spring singleton bean ถูกอธิบายเป็น 'ต่อคอนเทนเนอร์ต่อถั่ว' ขอบเขต Singleton ใน Spring หมายความว่าถั่วนี้จะถูกสร้างอินสแตนซ์เพียงครั้งเดียวใน Spring Spring container เพียงส่งคืนอินสแตนซ์เดียวกันซ้ำแล้วซ้ำอีกสำหรับการเรียกรับ bean ในภายหลัง


13
คุณเป็น 'java maverick' ใช่ไหม? นั่นจะทำให้คำพูดของคุณ "พบคำอธิบายและตัวอย่างที่ดีที่ ... " ความพยายามที่ไม่สุจริตในการปกปิดว่าคุณกำลังเชื่อมโยงไปยังเว็บไซต์ของคุณเอง ลิงก์ของคุณดูเหมือนจะไม่สำคัญสำหรับคำตอบอย่างไรก็ตาม ฉันกำลังลบออกเพื่อหลีกเลี่ยงไม่ให้คำตอบถูกลบเป็นสแปม โปรดอ่านคำถามที่พบบ่อยเกี่ยวกับการโปรโมตตนเองก่อนที่จะโพสต์ลิงก์ไปยังเว็บไซต์ของคุณอีก โปรดทราบว่าการใส่ลิงค์เว็บไซต์ในโปรไฟล์ของคุณนั้นค่อนข้างดี
Andrew Barber

2

Spring Singleton Bean อธิบายว่า 'ต่อภาชนะต่อถั่ว' ขอบเขต Singleton ใน Spring หมายความว่าวัตถุเดียวกันที่ตำแหน่งหน่วยความจำเดียวกันจะถูกส่งกลับไปยัง bean id เดียวกัน หากมีการสร้าง Bean หลายรหัสที่มีรหัสต่างกันของคลาสเดียวกันคอนเทนเนอร์จะส่งคืนอ็อบเจ็กต์ที่แตกต่างกันไปยังรหัสที่ต่างกัน นี่เปรียบเสมือนการแมปค่าคีย์โดยที่คีย์คือรหัสถั่วและค่าคืออ็อบเจ็กต์ bean ในคอนเทนเนอร์สปริงหนึ่งอัน โดยที่รูปแบบ Singleton ช่วยให้มั่นใจได้ว่าจะมีการสร้างอินสแตนซ์ของคลาสเฉพาะหนึ่งอินสแตนซ์ต่อ classloader


2

อย่างน้อยคำตอบทั้งหมดก็มุ่งเน้นไปที่การอธิบายความแตกต่างระหว่างรูปแบบการออกแบบและ Spring singleton และไม่ได้ตอบคำถามที่แท้จริงของคุณ: ควรใช้รูปแบบการออกแบบ Singleton หรือ Spring singleton bean หรือไม่? อะไรดีกว่า?

ก่อนที่ฉันจะตอบให้ฉันระบุว่าคุณสามารถทำได้ทั้งสองอย่าง คุณสามารถใช้ bean เป็นรูปแบบการออกแบบ Singleton และใช้ Spring เพื่อฉีดเข้าคลาสไคลเอนต์เป็น Spring singleton bean

ตอนนี้คำตอบสำหรับคำถามนั้นง่ายมาก: อย่าใช้รูปแบบการออกแบบ Singleton!
ใช้ Singleton bean ของ Spring ที่ใช้เป็นคลาสที่มีตัวสร้างสาธารณะ
ทำไม? เนื่องจากรูปแบบการออกแบบ Singleton ถือเป็นรูปแบบการต่อต้าน ส่วนใหญ่เป็นเพราะการทดสอบมีความซับซ้อน (และถ้าคุณไม่ได้ใช้ Spring เพื่อฉีดมันคลาสทั้งหมดที่ใช้ซิงเกิลตันจะถูกผูกไว้อย่างแน่นหนา) และคุณจะไม่สามารถแทนที่หรือขยายมันได้ เราสามารถ google "Singleton anti-pattern" เพื่อรับข้อมูลเพิ่มเติมเกี่ยวกับเรื่องนี้เช่นSingleton anti-pattern

การใช้ Spring singleton เป็นวิธีที่จะไป (ด้วยการใช้ singleleton bean ไม่ใช่รูปแบบการออกแบบ Singleton แต่ใช้ตัวสร้างสาธารณะ) เพื่อให้สามารถทดสอบ Spring singleton bean ได้อย่างง่ายดายและคลาสที่ใช้ไม่ได้เชื่อมต่อกันอย่างแน่นหนา แต่ Spring จะฉีด Singleton (เป็นอินเทอร์เฟซ) ลงในถั่วทั้งหมดที่ต้องการและสามารถเปลี่ยน Singleton bean ได้ทุกเมื่อด้วยการใช้งานอื่นโดยไม่ส่งผลกระทบต่อคลาสไคลเอ็นต์ที่ใช้


1

"singleton" ในฤดูใบไม้ผลิใช้อินสแตนซ์รับถั่วจากนั้นแคช; รูปแบบการออกแบบซิงเกิลตันใดที่เคร่งครัดอินสแตนซ์สามารถเรียกคืนได้จากวิธีรับแบบคงที่เท่านั้นและวัตถุจะไม่สามารถสร้างอินสแตนซ์ต่อสาธารณะได้


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"> </ คุณสมบัติ> </bean> <bean class = "com.spring4hibernate4.TestBean"> <constructor-arg name = "i" value = "10"> </constructor-arg> <property name = "name" value = "10 -name "> </property> </bean> </beans>
Hariprasad
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.