ContextLoaderListener หรือไม่?


122

แอพลิเคชันเว็บฤดูใบไม้ผลิมาตรฐาน (สร้างโดยกินตานาโรหรือ "ฤดูใบไม้ผลิ MVC โครงการ" แม่แบบ) สร้าง web.xml ด้วยและContextLoaderListener ทำไมพวกเขาไม่เพียงใช้และทำให้มันโหลดการกำหนดค่าที่สมบูรณ์?DispatcherServletDispatcherServlet

ฉันเข้าใจว่าควรใช้ ContextLoaderListener เพื่อโหลดสิ่งที่ไม่เกี่ยวข้องกับเว็บและใช้ DispatcherServlet เพื่อโหลดสิ่งที่เกี่ยวข้องกับเว็บ (Controllers, ... ) และส่งผลให้เกิดสองบริบท: บริบทผู้ปกครองและบริบทย่อย

พื้นหลัง:

ฉันทำแบบนี้มาหลายปีแล้ว

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath*:META-INF/spring/applicationContext*.xml</param-value>
</context-param>

<!-- Creates the Spring Container shared by all Servlets and Filters -->
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<!-- Handles Spring requests -->
<servlet>
    <servlet-name>roo</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>WEB-INF/spring/webmvc-config.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

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

- อย่างไรก็ตามสิ่งนี้ทำให้ฉันคิดทบทวนรูปแบบบริบททั้งสองนี้ใหม่และฉันก็ถามตัวเองว่า: ทำไมฉันต้องพาตัวเองไปสู่ปัญหานี้ทำไมไม่โหลดไฟล์คอนฟิกูเรชันสปริงทั้งหมดด้วยไฟล์เดียวDispatcherServletและลบไฟล์ContextLoaderListener. (ฉันจะยังคงมีไฟล์การกำหนดค่าที่แตกต่างกัน แต่มีเพียงบริบทเดียว)

มีเหตุผลใดที่จะไม่นำออกContextLoaderListener?


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

1
@Andy - ในขณะที่ฉันมีความเห็นอกเห็นใจกับมุมมองนี้ แต่ฉันอดสังเกตไม่ได้ว่ากรณีการใช้งานที่คุณต้องการทั้งสองบริบท (การแชร์ออบเจ็กต์ระหว่างตัวกรองความปลอดภัยและ servlets การจัดการธุรกรรมโดยอัตโนมัติเพื่อให้ปิดหลังจากการดู ที่คุณเปลี่ยนเส้นทางไปแสดงผลเสร็จสิ้นแล้ว) ค่อนข้างยากที่จะบรรลุโดยไม่ต้องใช้กรอบ ส่วนใหญ่เป็นเพราะ servlet API นั้นไม่เคยถูกออกแบบมาให้ทำงานร่วมกับการฉีดแบบพึ่งพาเลยและจะทำงานกับคุณอย่างแข็งขันหากคุณพยายามทำด้วยตัวเอง
Periata Breatta

@PeriataBreatta ฉันเห็น! คุณคิดว่าถ้าได้รับการออกแบบให้แตกต่างไปจากนี้จะมีทางเลือกอื่นที่ดีกว่าสำหรับ Spring MVC หรือไม่? แม้ว่าผู้คนจะสามารถออกแบบทางเลือกที่สมบูรณ์ให้กับ Servlet API ได้ก็ตาม ...
Andy

@PeriataBreatta เป็นเรื่องที่น่าสนใจที่จะทราบว่าในโลกของ JS ซึ่งฉันใช้ Express เพื่อกำหนดเส้นทางคำขอ HTTP มาประมาณหนึ่งปีแล้วฉันแทบไม่เห็นการกล่าวถึง "การฉีดแบบพึ่งพา" และไม่มีอะไรที่คล้ายกับ Spring framework เลย
Andy

คำตอบ:


86

ในกรณีของคุณไม่มีเหตุผลที่จะให้ไม่มีและContextLoaderListener applicationContext.xmlหากแอปของคุณทำงานได้ดีกับบริบทของ servlet สิ่งนั้นจะง่ายกว่า

ใช่รูปแบบที่ได้รับการสนับสนุนโดยทั่วไปคือการเก็บสิ่งที่ไม่ใช่เว็บไว้ในบริบทระดับเว็บแอป แต่ไม่มีอะไรมากไปกว่าการประชุมที่อ่อนแอ

เหตุผลเดียวที่น่าสนใจในการใช้บริบทระดับเว็บแอปคือ:

  • หากคุณมีหลายรายการDispatcherServletที่ต้องแชร์บริการ
  • หากคุณมี servlets แบบเดิม / ไม่ใช่ Spring ที่ต้องการเข้าถึงบริการ Spring-Wired
  • หากคุณมีตัวกรองเซิร์ฟเล็ตว่าเบ็ดลงในบริบท webapp ระดับ (เช่นฤดูใบไม้ผลิการรักษาความปลอดภัยของDelegatingFilterProxy, OpenEntityManagerInViewFilterฯลฯ )

สิ่งเหล่านี้ไม่มีผลกับคุณดังนั้นความซับซ้อนพิเศษจึงไม่ได้รับการรับรอง

โปรดใช้ความระมัดระวังในการเพิ่มงานพื้นหลังในบริบทของ servlet เช่นงานที่กำหนดเวลาไว้การเชื่อมต่อ JMS ฯลฯ หากคุณลืมเพิ่มลง<load-on-startup>ในของคุณweb.xmlงานเหล่านี้จะไม่เริ่มต้นจนกว่าจะมีการเข้าถึง servlet ครั้งแรก


2
สิ่งที่เกี่ยวกับผู้ฟังมันต้องมีการสร้างบริบทโดยผู้ฟัง Context Loader (IllegalStateException ไม่พบ WebApplicationContext ทริกเกอร์โดย MultipartFilter, CharacterEncodingFilter, HiddenHttpMethodFilter, Spring Security DelegatingFilterProxy และ OpenEntityManagerInViewFilter) เป็นความคิดที่ดีที่จะทำในทางอื่น (โหลดทุกสิ่งโดย ContextLoaderListener และปล่อยให้ DispatcherServlet โดยไม่มีการกำหนดค่า)
Ralph

@ ราล์ฟ: จับได้ดีฉันได้เพิ่มกรณีการใช้งานนั้นในรายการ สำหรับการออกDispatcherServletโดยไม่มีการกำหนดค่า - หากคุณทำเช่นนั้นคุณจะไม่มีเว็บอินเตอร์เฟส MVC ทุกอย่างต้องไปที่นั่น
skaffman

2
@skaffman เหตุใดฉันจึงควรใช้สองบริบทเมื่อใช้ spring-security กับ DelegatingFilterProxy ในกรณีของฉัน spring-security beans และบริบทของสปริงเริ่มต้นจะแบ่งปันถั่วบางส่วน ดังนั้นพวกเขาควรแบ่งปันบริบทเดียวกันด้วย หรือควรเก็บถั่วรักษาความปลอดภัยจากสปริงเริ่มต้น?
Matthias M

10

คุณสามารถกำหนดค่าบริบทของแอปพลิเคชันในทางกลับกันได้เช่นกัน เช่นเพื่อให้OpenEntityManagerInViewFilterทำงาน ตั้งค่าContextLoaderListenerจากนั้นกำหนดค่า DispatcherServlet ของคุณด้วย:

<servlet>
    <servlet-name>spring-mvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value></param-value>
    </init-param>
</servlet>

ตรวจสอบให้แน่ใจว่าค่าพารามิเตอร์contextConfigLocationว่างเปล่า


1
แต่ข้อดีของการกำหนดค่านี้คืออะไร? และคุณหมายถึงอะไร "ทางอื่น ๆ "?
Ralph

โซลูชันโดย "skaffman" กำหนดค่าบริบทเว็บแอ็พพลิเคชัน (servlet) เท่านั้น อย่างไรก็ตามด้วยวิธีการดังกล่าวคุณจะพบปัญหาตามรายละเอียดในการแก้ปัญหา: "เหตุผลเดียวที่น่าสนใจในการใช้บริบทระดับเว็บแอปคือ:" ... "หากคุณมีตัวกรอง servlet ที่เชื่อมโยงเข้ากับบริบทระดับ webbapp (เช่น DelegatingFilterProxy ของ Spring Security, OpenEntityManagerInViewFilter ฯลฯ ) "ถ้าคุณต้องการใช้ไฟล์ XML บริบทของแอปพลิเคชัน 1 ไฟล์เท่านั้นฉันคิดว่าวิธีแก้ปัญหาของฉัน (ระบุ XML ผ่าน ContextLoaderListener) น่าจะดีกว่า
Gunnar Hillert

คุณสามารถใช้ MVC Web Controller ในบริบทที่สร้างโดย Context Listener ได้หรือไม่
Ralph

1
ใช่. คุณเพียงแค่ตั้งค่าคอนโทรลเลอร์ของคุณในไฟล์ context.xml ที่ระบุโดย Context Listener วิธีการทำงานคือ DispatcherServlet จะเข้าร่วม "บริบทแอปพลิเคชันหลัก" (Context Listener) เมื่อคุณปล่อยให้ค่า "contextConfigLocation" ว่างไว้ไฟล์ context.xml ที่ระบุโดย Context Listener จะถูกใช้โดยเฉพาะ
Gunnar Hillert

1
ฉันคิดว่าคุณพลาด <mvc: annotation-driven /> ในบริบทของคุณ โซลูชัน @GunnarHillert ใช้ได้กับฉัน
milbr

10

ฉันต้องการแบ่งปันสิ่งที่ฉันทำในแอปพลิเคชัน Spring-MVC ของฉัน:

  1. บนwe-mvc-config.xmlผมเพิ่มเพียงแค่เรียนกับข้อเขียน @Controller:

    <context:component-scan base-package="com.shunra.vcat">
        <context:include-filter expression="org.springframework.stereotype.Controller" type="annotation"/>
    </context:component-scan>
  2. ในapplicationContext.xmlไฟล์ฉันเพิ่มส่วนที่เหลือทั้งหมด:

    <context:component-scan base-package="com.shunra.vcat">
        <context:exclude-filter expression="org.springframework.stereotype.Controller" type="annotation"/>
    </context:component-scan>

ใช่นี่เป็นรูปแบบที่มีประโยชน์ อีกรูปแบบหนึ่งที่มีประโยชน์คือการใส่ถั่วจัดการฐานข้อมูลของคุณในบริบทของแอปพลิเคชัน (สิ่งเหล่านี้จำเป็นสำหรับ OpenSessionInViewFilter หรือที่คล้ายกัน) พร้อมกับสิ่งที่ต้องการโดยเฉพาะโดยตัวกรองหรือผู้ฟัง (เช่นคำจำกัดความที่จำเป็นในการใช้การรักษาความปลอดภัยสปริง)
Periata Breatta
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.