ทำไม ApplicationContext.getBean ของสปริงจึงถือว่าไม่ดี


270

ฉันถามคำถามทั่วไปเกี่ยวกับ Spring: Auto Spring Beansและมีหลายคนตอบว่าApplicationContext.getBean()ควรหลีกเลี่ยงการเรียกของ Spring ให้มากที่สุด ทำไมถึงเป็นอย่างนั้น?

ฉันจะเข้าใช้งาน beans ที่ฉันกำหนดค่า Spring เพื่อสร้างได้อย่างไร

ฉันใช้ฤดูใบไม้ผลิในโปรแกรมประยุกต์ที่ไม่ใช่เว็บและได้วางแผนเกี่ยวกับการเข้าถึงที่ใช้ร่วมกันApplicationContextวัตถุตามที่อธิบาย LiorH

การแก้ไข

ฉันยอมรับคำตอบด้านล่าง แต่มาร์ตินฟาวเลอร์เป็นอีกทางเลือกหนึ่งที่กล่าวถึงข้อดีของการพึ่งพาการฉีดเทียบกับการใช้ตัวระบุบริการ (ซึ่งเป็นหลักเหมือนกับการโทรด้วยการห่อหุ้มApplicationContext.getBean())

ในส่วนของ Fowler กล่าวว่า " ด้วย service locator คลาสแอปพลิเคชันจะขอให้มัน [บริการ] อย่างชัดเจนโดยข้อความถึง locator ด้วยการฉีดไม่มีการร้องขอที่ชัดเจนบริการจะปรากฏในคลาสแอปพลิเคชัน - ดังนั้นการควบคุม การกลับกันของการควบคุมเป็นคุณสมบัติทั่วไปของกรอบงาน แต่เป็นสิ่งที่เกิดขึ้นในราคามันมีแนวโน้มที่จะเข้าใจและนำไปสู่ปัญหาเมื่อคุณพยายามที่จะทำการดีบั๊กดังนั้นโดยรวมแล้วฉันชอบที่จะหลีกเลี่ยงมัน ] ถ้าฉันต้องการมัน. นี้ไม่ได้บอกว่ามันเป็นสิ่งที่ไม่ดีเพียงแค่ว่าผมคิดว่าจะต้องมีการปรับตัวเองมากกว่าทางเลือกที่ตรงไปตรงมามากขึ้น. "

คำตอบ:


202

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

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

ทุกที่ที่คุณอยากพูด:

MyClass myClass = applicationContext.getBean("myClass");

ตัวอย่างเช่นคุณควรประกาศวิธี:

public void setMyClass(MyClass myClass) {
   this.myClass = myClass;
}

จากนั้นในการกำหนดค่าของคุณ:

<bean id="myClass" class="MyClass">...</bean>

<bean id="myOtherClass" class="MyOtherClass">
   <property name="myClass" ref="myClass"/>
</bean>

ฤดูใบไม้ผลิแล้วจะฉีดโดยอัตโนมัติลงในmyClassmyOtherClass

ประกาศทุกอย่างด้วยวิธีนี้และที่รากของมันทุกคนมีสิ่งที่ชอบ:

<bean id="myApplication" class="MyApplication">
   <property name="myCentralClass" ref="myCentralClass"/>
   <property name="myOtherCentralClass" ref="myOtherCentralClass"/>
</bean>

MyApplicationเป็นชั้นกลางที่สุดและขึ้นอยู่กับบริการอื่น ๆ ในโปรแกรมของคุณ เมื่อทำการ bootstrapping ในmainวิธีการของคุณคุณสามารถโทรออกapplicationContext.getBean("myApplication")แต่คุณไม่จำเป็นต้องโทรหาgetBean()ที่อื่น!


3
มีสิ่งใดที่เกี่ยวข้องกับสิ่งนี้ที่ใช้งานได้กับการเพิ่มความคิดเห็นเพียงอย่างเดียวเมื่อสร้างnew MyOtherClass()วัตถุหรือไม่? ฉันรู้เกี่ยวกับ @Autowired แต่ฉันเท่านั้นที่เคยใช้มันในทุ่งนาและจะแบ่งบนnew MyOtherClass()..
ทิม

70
ไม่เป็นความจริงที่ ApplicationContext.getBean () ไม่ใช่ IoC Niether จำเป็นต้องให้ทุกคลาสของคุณสร้างอินสแตนซ์โดย Spring นั่นคือความเชื่อที่ไม่เหมาะสม ถ้า ApplicationContext ถูกฉีดเข้ามามันก็เป็นเรื่องที่สมบูรณ์แบบที่จะขอให้มันสร้างอินสแตนซ์ของถั่วด้วยวิธีนี้และถั่วที่มันสร้างขึ้นนั้นสามารถนำไปใช้งานที่แตกต่างกันได้โดยขึ้นอยู่กับ ApplicationContext ในตอนแรก ตัวอย่างเช่นฉันมีสถานการณ์ที่ฉันสร้างอินสแตนซ์ bean ใหม่แบบไดนามิกโดยใช้ชื่อ bean ที่ไม่รู้จัก ณ เวลารวบรวม แต่ตรงกับการนำไปใช้งานอย่างใดอย่างหนึ่งที่กำหนดไว้ในไฟล์ spring.xml ของฉัน
Alex Worden

3
เห็นด้วยกับอเล็กซ์, ฉันมีปัญหาเดียวกันที่ระดับโรงงานเท่านั้นที่จะทราบว่าถั่วหรือการดำเนินการที่จะใช้ในเวลาทำงานผ่านปฏิสัมพันธ์ของผู้ใช้ผมคิดว่านี่เป็นที่ที่อินเตอร์เฟซ ContextAware มาใน
เบน

3
@elbek: applicationContext.getBeanไม่ได้พึ่งพาการฉีด: มันเข้าถึงกรอบโดยตรงใช้มันเป็นบริการค้นหา
ColinD

6
@herman: ฉันไม่รู้เกี่ยวกับ Spring เพราะฉันไม่ได้ใช้มันมานาน แต่ใน JSR-330 / Guice / Dagger คุณต้องทำสิ่งนี้ด้วยการฉีดProvider<Foo>แทนFooและเรียกprovider.get()ทุกครั้งที่คุณต้องการ ตัวอย่างใหม่ ไม่มีการอ้างอิงถึงคอนเทนเนอร์และคุณสามารถสร้างการProviderทดสอบได้อย่างง่ายดาย
ColinD

64

เหตุผลที่ชอบผู้ให้บริการมากกว่าการควบคุมแบบผกผัน (IoC) คือ:

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

  2. IoC แย่มากสำหรับการแก้ไขปัญหาการตั้งค่า ในแอปพลิเคชันบางประเภทแอปพลิเคชันจะไม่เริ่มทำงานเมื่อกำหนดค่าผิดพลาดและคุณอาจไม่ได้รับโอกาสผ่านขั้นตอนการทำงานกับตัวดีบัก

  3. IoC นั้นใช้ XML เป็นหลัก (หมายเหตุประกอบจะปรับปรุงสิ่งต่าง ๆ แต่ยังมี XML จำนวนมากอยู่) นั่นหมายความว่าผู้พัฒนาไม่สามารถทำงานกับโปรแกรมของคุณได้เว้นแต่พวกเขาจะรู้ว่าแท็กเวทย์มนตร์ทั้งหมดที่สปริง มันไม่ดีพอที่จะรู้ Java อีกต่อไป นี่เป็นอุปสรรคต่อโปรแกรมเมอร์ที่มีประสบการณ์น้อยกว่า (เช่นจริง ๆ แล้วมันเป็นการออกแบบที่ไม่ดีที่จะใช้โซลูชั่นที่ซับซ้อนมากขึ้นเมื่อโซลูชันที่ง่ายกว่าเช่น Service Locator จะตอบสนองความต้องการเดียวกัน) นอกจากนี้การรองรับการวินิจฉัยปัญหา XML นั้นอ่อนแอกว่าการสนับสนุนปัญหา Java

  4. การฉีดพึ่งพานั้นเหมาะกับโปรแกรมขนาดใหญ่ ส่วนใหญ่แล้วความซับซ้อนเพิ่มเติมนั้นไม่คุ้มค่า

  5. มักใช้ Spring ในกรณีที่คุณ "อาจต้องการเปลี่ยนการใช้งานในภายหลัง" มีวิธีอื่นในการบรรลุเป้าหมายนี้โดยไม่มีความซับซ้อนของ Spring IoC

  6. สำหรับเว็บแอปพลิเคชัน (Java EE WARs) บริบท Spring จะถูกรวมในเวลาคอมไพล์ได้อย่างมีประสิทธิภาพ (ยกเว้นว่าคุณต้องการให้โอเปอเรเตอร์ด้วงรอบบริบทในสงครามระเบิด) คุณสามารถสร้างไฟล์คุณสมบัติ Spring ใช้ แต่ด้วยไฟล์คุณสมบัติ servlets จะต้องอยู่ในตำแหน่งที่กำหนดไว้ล่วงหน้าซึ่งหมายความว่าคุณไม่สามารถปรับใช้หลาย servlets ในเวลาเดียวกันในกล่องเดียวกัน คุณสามารถใช้ Spring กับ JNDI เพื่อเปลี่ยนคุณสมบัติเมื่อเวลาเริ่มต้น servlet แต่ถ้าคุณกำลังใช้ JNDI สำหรับพารามิเตอร์ที่ผู้ดูแลระบบสามารถแก้ไขได้ความต้องการของ Spring จะลดลง (ตั้งแต่ JNDI เป็นตัวระบุบริการได้อย่างมีประสิทธิภาพ)

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

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


7
นอกจากนี้ ServiceLocator ของคุณสามารถใช้ IoC จาก Spring ได้โดยการย่อโค้ดของคุณจากการพึ่งพา Spring, ปูหญ้าด้วยคำอธิบายประกอบ Spring และ magik ที่ไม่สามารถถอดรหัสได้ ฉันเพิ่งย้ายโค้ดไปที่ GoogleAppEngine โดยที่ไม่รองรับ Spring ฉันหวังว่าฉันจะซ่อน IoC ทั้งหมดไว้ข้างหลัง ServiceFactory ตั้งแต่แรก!
Alex Worden

IoC ส่งเสริมรูปแบบโดเมนโลหิตจางซึ่งฉันดูหมิ่น Entity beans ต้องการวิธีในการค้นหาบริการของตนเองเพื่อให้สามารถใช้พฤติกรรมของตนเองได้ ที่เลเยอร์นั้นคุณไม่สามารถหลีกเลี่ยงความต้องการบริการค้นหาได้
Joel

4
Bizar ฉันใช้สปริงตลอดเวลาพร้อมคำอธิบายประกอบ แม้ว่าจะมีช่วงการเรียนรู้ที่แน่นอน แต่ตอนนี้ฉันไม่มีปัญหาใด ๆ ในการบำรุงรักษาการดีบักความชัดเจนการอ่านได้ .... ฉันเดาว่าคุณจะจัดโครงสร้างสิ่งต่าง ๆ ได้อย่างไร
Lawrence

25

เป็นความจริงที่รวมคลาสใน application-context.xml เพื่อหลีกเลี่ยงความจำเป็นในการใช้ getBean อย่างไรก็ตามถึงแม้จะไม่จำเป็นจริงๆ หากคุณกำลังเขียนแอปพลิเคชันแบบสแตนด์อโลนและคุณไม่ต้องการรวมคลาสไดร์เวอร์ของคุณใน application-context.xml คุณสามารถใช้รหัสต่อไปนี้เพื่อให้สปริงทำการพึ่งพาผู้ขับขี่โดยอัตโนมัติ:

public class AutowireThisDriver {

    private MySpringBean mySpringBean;    

    public static void main(String[] args) {
       AutowireThisDriver atd = new AutowireThisDriver(); //get instance

       ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(
                  "/WEB-INF/applicationContext.xml"); //get Spring context 

       //the magic: auto-wire the instance with all its dependencies:
       ctx.getAutowireCapableBeanFactory().autowireBeanProperties(atd,
                  AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, true);        

       // code that uses mySpringBean ...
       mySpringBean.doStuff() // no need to instantiate - thanks to Spring
    }

    public void setMySpringBean(MySpringBean bean) {
       this.mySpringBean = bean;    
    }
}

ฉันต้องทำสองสามครั้งเมื่อฉันมีคลาสแบบสแตนด์อโลนบางประเภทที่ต้องใช้บางส่วนของแอปของฉัน (เช่นสำหรับการทดสอบ) แต่ฉันไม่ต้องการรวมไว้ในแอปพลิเคชันบริบทเนื่องจากไม่ใช่ เป็นส่วนหนึ่งของแอปจริง ๆ โปรดทราบว่าสิ่งนี้หลีกเลี่ยงความต้องการในการค้นหา bean โดยใช้ชื่อ String ซึ่งฉันคิดว่าน่าเกลียดอยู่เสมอ


ฉันสามารถใช้วิธีนี้ได้สำเร็จพร้อม@Autowiredคำอธิบายประกอบด้วย
blong

21

หนึ่งในข้อดีที่สุดของการใช้บางอย่างเช่นสปริงคือคุณไม่ต้องต่อสายเข้าด้วยกัน หัวแยกของซุสเปิดและชั้นเรียนของคุณจะปรากฏขึ้นเต็มไปด้วยการพึ่งพาทั้งหมดที่สร้างขึ้นและเชื่อมต่อแบบมีสายตามต้องการ มันวิเศษและมหัศจรรย์

ยิ่งคุณพูดมากขึ้น ClassINeed classINeed = (ClassINeed)ApplicationContext.getBean("classINeed");มากเท่าไหร่คุณก็ยิ่งได้รับเวทมนตร์น้อยลงเท่านั้น รหัสน้อยลงเกือบจะดีกว่าเสมอ หากชั้นเรียนของคุณต้องการถั่ว ClassINeed ทำไมคุณไม่ลองใช้มันดูล่ะ

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


1
แต่ OP ไม่ได้บอกว่า "ClassINeed" เขากำลังพูดว่า "BeanNameINeed" - ซึ่งอนุญาตให้คอนเทนเนอร์ IoC สร้างอินสแตนซ์บนคลาสใด ๆ ที่กำหนดค่าในลักษณะใด ๆ บางทีมันอาจเหมือนกับรูปแบบ "service locator" มากกว่า IoC แต่ก็ยังส่งผลให้เกิดการแต่งงานกันแบบหลวม ๆ
HDave

16

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

คิดว่าคอนเทนเนอร์เป็นสิ่งที่มองไม่เห็นรหัสของคุณอย่างน่าอัศจรรย์ให้กับความต้องการโดยไม่ต้องถาม

การพึ่งพาการฉีดเป็นความแตกต่างในรูปแบบ "บริการระบุตำแหน่ง" หากคุณกำลังจะค้นหาการอ้างอิงตามชื่อคุณอาจกำจัด DI container และใช้บางอย่างเช่น JNDI


11

การใช้@AutowiredหรือApplicationContext.getBean()เป็นสิ่งเดียวกัน ในทั้งสองวิธีคุณจะได้รับ bean ที่กำหนดค่าในบริบทของคุณและในทั้งสองวิธีโค้ดของคุณขึ้นอยู่กับสปริง สิ่งเดียวที่คุณควรหลีกเลี่ยงก็คือ ทำเช่นนี้เพียงครั้งเดียว! กล่าวอีกนัยหนึ่งบรรทัดเหมือน

ApplicationContext context = new ClassPathXmlApplicationContext("AppContext.xml");

ควรใช้เพียงครั้งเดียวในแอปพลิเคชันของคุณ


Nope บางครั้ง @Autowired หรือ ApplicationContext.getBean () สามารถสร้างถั่วที่แตกต่างกันโดยสิ้นเชิง ฉันไม่แน่ใจว่ามันเกิดขึ้นได้อย่างไร แต่ฉันกำลังดิ้นรนกับปัญหานี้ในตอนนี้
Oleksandr_DJ

4

แนวคิดก็คือคุณต้องพึ่งพาการฉีดพึ่งพา ( การผกผันของการควบคุมหรือ IoC) นั่นคือส่วนประกอบของคุณจะถูกกำหนดค่าด้วยส่วนประกอบที่ต้องการ การพึ่งพาเหล่านี้จะถูกฉีด (ผ่านคอนสตรัคหรือผู้ตั้งค่า) - คุณจะไม่ได้รับตัวคุณเอง

ApplicationContext.getBean()คุณต้องตั้งชื่อถั่วให้ชัดเจนภายในองค์ประกอบของคุณ โดยใช้ IoC แทนการกำหนดค่าของคุณสามารถกำหนดองค์ประกอบที่จะใช้

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


4

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

โดยทั่วไปนี่หมายความว่าทำครั้งเดียวในระหว่างการบูตสแตรป จากนั้นเป็นเพียงการเข้าถึงถั่ว "รูท" ซึ่งการพึ่งพาอื่น ๆ สามารถแก้ไขได้ นี่อาจเป็นรหัสที่ใช้ซ้ำได้เช่น servlet ฐาน (หากพัฒนาแอปพลิเคชันเว็บ)


4

หนึ่งในสถานที่ในฤดูใบไม้ผลิคือหลีกเลี่ยงการแต่งงานกัน กำหนดและใช้ส่วนต่อประสาน DI, AOP และหลีกเลี่ยงการใช้ ApplicationContext.getBean () :-)


4

เหตุผลหนึ่งคือการตรวจสอบ สมมติว่าคุณมีคลาสนี้:

interface HttpLoader {
    String load(String url);
}
interface StringOutput {
    void print(String txt);
}
@Component
class MyBean {
    @Autowired
    MyBean(HttpLoader loader, StringOutput out) {
        out.print(loader.load("http://stackoverflow.com"));
    }
}

คุณจะทดสอบถั่วนี้ได้อย่างไร เช่นนี้

class MyBeanTest {
    public void creatingMyBean_writesStackoverflowPageToOutput() {
        // setup
        String stackOverflowHtml = "dummy";
        StringBuilder result = new StringBuilder();

        // execution
        new MyBean(Collections.singletonMap("https://stackoverflow.com", stackOverflowHtml)::get, result::append);

        // evaluation
        assertEquals(result.toString(), stackOverflowHtml);
    }
}

ง่ายใช่มั้ย

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

ยังคงเป็นไปได้ที่จะทำเช่นเดียวกันเมื่อใช้ ApplicationContext อย่างไรก็ตามคุณต้องจำลองApplicationContextซึ่งเป็นอินเตอร์เฟสขนาดใหญ่ คุณอาจต้องใช้การจำลองแบบหรือคุณสามารถใช้กรอบการเยาะเย้ยเช่น Mockito:

@Component
class MyBean {
    @Autowired
    MyBean(ApplicationContext context) {
        HttpLoader loader = context.getBean(HttpLoader.class);
        StringOutput out = context.getBean(StringOutput.class);

        out.print(loader.load("http://stackoverflow.com"));
    }
}
class MyBeanTest {
    public void creatingMyBean_writesStackoverflowPageToOutput() {
        // setup
        String stackOverflowHtml = "dummy";
        StringBuilder result = new StringBuilder();
        ApplicationContext context = Mockito.mock(ApplicationContext.class);
        Mockito.when(context.getBean(HttpLoader.class))
            .thenReturn(Collections.singletonMap("https://stackoverflow.com", stackOverflowHtml)::get);
        Mockito.when(context.getBean(StringOutput.class)).thenReturn(result::append);

        // execution
        new MyBean(context);

        // evaluation
        assertEquals(result.toString(), stackOverflowHtml);
    }
}

นี่เป็นความเป็นไปได้ค่อนข้างมาก แต่ฉันคิดว่าคนส่วนใหญ่ยอมรับว่าตัวเลือกแรกนั้นดูดีกว่าและทำให้การทดสอบง่ายขึ้น

ตัวเลือกเดียวที่เป็นปัญหาจริงๆคือตัวเลือกนี้:

@Component
class MyBean {
    @Autowired
    MyBean(StringOutput out) {
        out.print(new HttpLoader().load("http://stackoverflow.com"));
    }
}

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

ดังนั้นโดยสรุปฉันจะไม่พูดว่าการใช้ApplicationContextโดยตรงนั้นผิดโดยอัตโนมัติและควรหลีกเลี่ยงค่าใช้จ่ายทั้งหมด อย่างไรก็ตามหากมีตัวเลือกที่ดีกว่า (และในกรณีส่วนใหญ่) ให้ใช้ตัวเลือกที่ดีกว่า


3

ฉันพบเพียงสองสถานการณ์ที่จำเป็นต้องใช้ getBean ():

คนอื่น ๆ พูดถึงการใช้ getBean () ใน main () เพื่อดึง bean "main" สำหรับโปรแกรมแบบสแตนด์อโลน

การใช้งานอื่นที่ฉันได้ทำจาก getBean () อยู่ในสถานการณ์ที่การกำหนดค่าผู้ใช้แบบโต้ตอบกำหนด bean makeup สำหรับสถานการณ์เฉพาะ ตัวอย่างเช่นส่วนหนึ่งของระบบการบู๊ตจะวนลูปผ่านตารางฐานข้อมูลโดยใช้ getBean () พร้อมด้วยคำ จำกัด ขอบเขตถั่วต้นแบบ 'ต้นแบบ' จากนั้นตั้งค่าคุณสมบัติเพิ่มเติม สันนิษฐานว่ามี UI ที่ปรับตารางฐานข้อมูลที่จะเป็นมิตรกว่าการพยายาม (อีกครั้ง) เขียนบริบท XML ของแอปพลิเคชัน


3

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


2

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


โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.