ContextLoaderListener atau tidak?

122

Aplikasi web pegas standar (dibuat oleh Roo atau Template "Proyek MVC Musim Semi") membuat web.xml dengan ContextLoaderListenerdan DispatcherServlet. Mengapa mereka tidak hanya menggunakan DispatcherServletdan membuatnya untuk memuat konfigurasi lengkap?

Saya memahami bahwa ContextLoaderListener harus digunakan untuk memuat hal-hal yang tidak relevan dengan web dan DispatcherServlet digunakan untuk memuat hal-hal yang relevan dengan web (Pengontrol, ...). Dan ini menghasilkan dua konteks: konteks orang tua dan anak.

Latar Belakang:

Saya melakukannya dengan cara standar ini selama beberapa tahun.

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

Hal ini seringkali menimbulkan masalah pada kedua konteks dan ketergantungan di antara keduanya. Di masa lalu saya selalu dapat menemukan solusi, dan saya memiliki perasaan kuat bahwa ini membuat struktur / arsitektur perangkat lunak selalu lebih baik. Tapi sekarang saya menghadapi masalah dengan kejadian dari kedua konteks tersebut .

- Namun ini membuat saya memikirkan ulang dua pola konteks ini, dan saya bertanya pada diri sendiri: mengapa saya harus membawa diri saya ke dalam masalah ini, mengapa tidak memuat semua file konfigurasi pegas dengan satu DispatcherServletdan menghapus ContextLoaderListenersepenuhnya. (Saya masih akan memiliki file konfigurasi yang berbeda, tetapi hanya satu konteks.)

Apakah ada alasan untuk tidak menghapus ContextLoaderListener?

Muntah
sumber
"Ini sering menimbulkan masalah dengan dua konteks dan ketergantungan di antara keduanya." Ini adalah contoh yang bagus tentang bagaimana menurut saya kerangka kerja injeksi ketergantungan hanya membuat hidup kita lebih sulit daripada injeksi ketergantungan do-it-yourself.
Andy
1
@Andy - Meskipun saya memiliki simpati dengan sudut pandang ini, saya tidak dapat membantu memperhatikan bahwa kasus penggunaan yang Anda perlukan kedua konteks (berbagi objek antara filter keamanan dan servlet, secara otomatis mengelola transaksi sehingga ditutup setelah tampilan yang Anda alihkan ke telah selesai merender) cukup sulit dicapai tanpa bantuan kerangka kerja. Ini sebagian besar karena servlet API jelas tidak pernah dirancang untuk bekerja dengan injeksi ketergantungan sama sekali, dan secara aktif bekerja melawan Anda jika Anda mencoba melakukannya sendiri.
Periata Breatta
@PeriataBreatta begitu! Nah, menurut Anda apakah jika dirancang berbeda maka akan ada alternatif yang lebih baik untuk Spring MVC? Meskipun orang bisa saja merancang alternatif lengkap untuk Servlet API ...
Andy
@PeriataBreatta Sangat menarik untuk dicatat bahwa di dunia JS, di mana saya telah menggunakan Express untuk merutekan permintaan HTTP selama sekitar satu tahun, saya jarang melihat penyebutan "injeksi ketergantungan" dan tidak ada yang menyerupai kerangka kerja Spring sama sekali.
Andy

Jawaban:

86

Dalam kasus Anda, tidak, tidak ada alasan untuk menyimpan ContextLoaderListenerdan applicationContext.xml. Jika aplikasi Anda berfungsi dengan baik hanya dengan konteks servlet, tetap dengan itu, itu lebih sederhana.

Ya, pola yang umumnya dianjurkan adalah menyimpan barang non-web dalam konteks tingkat aplikasi web, tetapi itu tidak lebih dari konvensi yang lemah.

Satu-satunya alasan kuat untuk menggunakan konteks tingkat webapp adalah:

  • Jika Anda memiliki beberapa DispatcherServletyang perlu berbagi layanan
  • Jika Anda memiliki servlet lama / non-Spring yang memerlukan akses ke layanan kabel Spring
  • Jika Anda memiliki filter servlet yang kait ke dalam konteks webapp tingkat (misalnya musim semi Security DelegatingFilterProxy, OpenEntityManagerInViewFilter, dll)

Tak satu pun dari ini berlaku untuk Anda, jadi kerumitan ekstra tidak beralasan.

Berhati-hatilah saat menambahkan tugas latar belakang ke konteks servlet, seperti tugas terjadwal, koneksi JMS, dll. Jika Anda lupa menambahkan <load-on-startup>ke web.xml, maka tugas ini tidak akan dimulai hingga akses pertama servlet.

skaffman
sumber
2
Bagaimana dengan pendengar, itu memastikan bahwa mereka membutuhkan Konteks yang dibuat oleh pendengar Context Loader (IllegalStateException, Tidak ditemukan WebApplicationContext, dipicu oleh MultipartFilter, CharacterEncodingFilter, HiddenHttpMethodFilter, Spring Security DelegatingFilterProxy dan OpenEntityManagerInViewFilter). Apakah ide yang baik untuk melakukannya dengan cara lain (Muat semuanya dengan ContextLoaderListener, dan biarkan DispatcherServlet tanpa konfigurasi)?
Ralph
@Ralph: Tangkapan bagus, saya telah menambahkan kasus penggunaan itu ke daftar. Sedangkan untuk pergi DispatcherServlettanpa konfigurasi - jika Anda melakukannya, Anda tidak akan memiliki antarmuka web. Semua barang MVC harus masuk ke sana.
Skaffman
2
@skaffman Mengapa saya harus menggunakan dua konteks saat menggunakan keamanan pegas dengan DelegatingFilterProxy? Dalam kasus saya, kacang keamanan pegas dan konteks pegas default berbagi beberapa kacang. Jadi mereka juga harus berbagi konteks yang sama. Atau haruskah kacang keamanan pegas disimpan di luar konteks pegas default?
Matthias M
10

Anda juga dapat mengkonfigurasi konteks aplikasi sebaliknya. Misalnya untuk membuat OpenEntityManagerInViewFilter berfungsi. Siapkan ContextLoaderListener lalu konfigurasikan DispatcherServlet Anda dengan:

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

Pastikan saja bahwa nilai parameter contextConfigLocation kosong.

Gunnar Hillert
sumber
1
Tetapi apa keuntungan dari konfigurasi ini? Dan apa yang Anda maksud dengan "sebaliknya"?
Ralph
Solusi dengan "skaffman" hanya mengkonfigurasi konteks aplikasi web (servlet). Namun, dengan pendekatan itu Anda mengalami masalah seperti yang dijelaskan dalam solusi itu sendiri: "Satu-satunya alasan kuat untuk menggunakan konteks tingkat webapp adalah:" ... "Jika Anda memiliki filter servlet yang menghubungkan ke konteks tingkat webbapp (mis. DelegatingFilterProxy, OpenEntityManagerInViewFilter, dll. Dari Spring Security "Jika Anda ingin menggunakan 1 file XML konteks aplikasi saja, saya pikir solusi saya (menentukan XML melalui ContextLoaderListener) akan lebih disukai.
Gunnar Hillert
apakah Anda dapat menggunakan MVC Web Controller dalam Konteks yang dibuat oleh Pemroses Konteks?
Ralph
1
Iya. Anda cukup menyiapkan pengontrol Anda dalam file context.xml yang ditentukan oleh Pemroses Konteks. Cara kerjanya adalah DispatcherServlet hanya akan bergabung dengan "konteks aplikasi induk" (Pemroses Konteks). Saat Anda membiarkan nilai "contextConfigLocation" kosong, file context.xml yang ditentukan oleh Pemroses Konteks akan digunakan secara eksklusif.
Gunnar Hillert
1
Sepertinya Anda melewatkan <mvc: annotation-driven /> dalam konteks Anda. Solusi @GunnarHillert bekerja untuk saya.
milbr
10

Saya ingin membagikan apa yang telah saya lakukan pada aplikasi Spring-MVC saya:

  1. Di we-mvc-config.xmlsaya menambahkan hanya kelas yang dianotasi dengan @Controller:

    <context:component-scan base-package="com.shunra.vcat">
        <context:include-filter expression="org.springframework.stereotype.Controller" type="annotation"/>
    </context:component-scan>
    
  2. Pada applicationContext.xmlfile saya menambahkan yang lainnya:

    <context:component-scan base-package="com.shunra.vcat">
        <context:exclude-filter expression="org.springframework.stereotype.Controller" type="annotation"/>
    </context:component-scan>
    
Modi
sumber
Ya, ini adalah pola yang berguna. Pola berguna lainnya adalah meletakkan bean penanganan database Anda dalam konteks aplikasi (ini mungkin diperlukan untuk OpenSessionInViewFilter atau serupa) bersama dengan apa pun yang secara khusus dibutuhkan oleh filter atau listener (misalnya definisi yang diperlukan untuk menggunakan keamanan pegas).
Periata Breatta