ContextLoaderListener hay không?


122

Một ứng dụng web mùa xuân tiêu chuẩn (được tạo bởi Roo hoặc Mẫu "Spring MVC Project") tạo một web.xml với ContextLoaderListenerDispatcherServlet. Tại sao họ không chỉ sử dụng DispatcherServletvà làm cho nó để tải cấu hình hoàn chỉnh?

Tôi hiểu rằng ContextLoaderListener nên được sử dụng để tải nội dung không liên quan đến web và DispatcherServlet được sử dụng để tải nội dung liên quan đến web (Bộ điều khiển, ...). Và điều này dẫn đến hai bối cảnh: bối cảnh cha và bối cảnh con.

Lý lịch:

Tôi đã làm theo cách tiêu chuẩn này trong vài năm.

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

Điều này thường gây ra vấn đề với hai ngữ cảnh và sự phụ thuộc giữa chúng. Trước đây, tôi luôn có thể tìm ra giải pháp và tôi có cảm giác mạnh mẽ rằng điều này làm cho cấu trúc / kiến ​​trúc phần mềm luôn tốt hơn. Nhưng bây giờ tôi đang phải đối mặt với một vấn đề với các sự kiện của cả hai bối cảnh .

- Tuy nhiên, điều này khiến tôi phải suy nghĩ lại về hai mẫu ngữ cảnh này và tôi đang tự hỏi bản thân: tại sao tôi phải tự đưa mình vào rắc rối này, tại sao không tải tất cả các tệp cấu hình mùa xuân bằng một DispatcherServletvà xóa ContextLoaderListenerhoàn toàn. (Tôi vẫn sẽ có các tệp cấu hình khác nhau, nhưng chỉ có một ngữ cảnh.)

Có lý do gì để không xóa ContextLoaderListener?


"Điều này thường gây ra vấn đề với hai bối cảnh và sự phụ thuộc giữa chúng." Đây là một ví dụ tuyệt vời về cách tôi nghĩ rằng các khuôn khổ tiêm phụ thuộc chỉ làm cho cuộc sống của chúng ta khó hơn so với việc tiêm phụ thuộc tự làm.
Andy

1
@Andy - Mặc dù tôi có một số thông cảm với quan điểm này, nhưng tôi không thể không nhận thấy rằng các trường hợp sử dụng mà bạn cần cả hai ngữ cảnh (chia sẻ các đối tượng giữa bộ lọc bảo mật và các servlet, tự động quản lý các giao dịch để chúng được đóng sau khi xem mà bạn chuyển hướng đến đã hoàn thành kết xuất) thì khá khó đạt được nếu không có sự trợ giúp của khuôn khổ. Điều này chủ yếu là do API servlet rõ ràng chưa bao giờ được thiết kế để hoạt động với việc tiêm phụ thuộc và chủ động chống lại bạn nếu bạn cố gắng tự làm điều đó.
Periata Breatta vào

@PeriataBreatta Tôi hiểu rồi! Chà, bạn có nghĩ rằng nếu nó được thiết kế khác biệt thì sẽ có những lựa chọn thay thế tốt hơn cho Spring MVC? Mặc dù mọi người có thể đã thiết kế các giải pháp thay thế hoàn chỉnh cho API Servlet ...
Andy

@PeriataBreatta Thật thú vị khi lưu ý rằng trong thế giới JS, nơi tôi đã sử dụng Express để định tuyến các yêu cầu HTTP trong khoảng một năm, tôi hiếm khi thấy bất kỳ đề cập nào về "tiêm phụ thuộc" và không có gì giống với Spring framework cả.
Andy

Câu trả lời:


86

Trong trường hợp của bạn, không, không có lý do gì để giữ ContextLoaderListenerapplicationContext.xml. Nếu ứng dụng của bạn hoạt động tốt chỉ với ngữ cảnh của servlet, thì việc gắn bó với điều đó, nó đơn giản hơn.

Vâng, mô hình thường được khuyến khích là giữ nội dung không phải web trong ngữ cảnh cấp ứng dụng web, nhưng nó không khác gì một quy ước yếu.

Các lý do thuyết phục duy nhất để sử dụng ngữ cảnh cấp ứng dụng web là:

  • Nếu bạn có nhiều DispatcherServletngười cần chia sẻ dịch vụ
  • Nếu bạn có các servlet cũ / không phải Spring cần truy cập vào các dịch vụ có dây của Spring
  • Nếu bạn có bộ lọc servlet mà móc vào bối cảnh webapp cấp (ví dụ như mùa xuân an nhân DelegatingFilterProxy, OpenEntityManagerInViewFilter, vv)

Không có điều nào trong số này áp dụng cho bạn, vì vậy sự phức tạp thêm là không có cơ sở.

Chỉ cần cẩn thận khi thêm các tác vụ nền vào ngữ cảnh của servlet, như các tác vụ đã lên lịch, các kết nối JMS, v.v. Nếu bạn quên thêm <load-on-startup>vào của mình web.xml, thì các tác vụ này sẽ không được bắt đầu cho đến khi có quyền truy cập đầu tiên của servlet.


2
Đối với trình nghe thì sao, nó kết hợp rằng họ cần Context do trình nghe Context Loader tạo (IllegalStateException, Không tìm thấy WebApplicationContext, được kích hoạt bởi MultipartFilter, CharacterEncodingFilter, HiddenHttpMethodFilter, Spring Security DelegateFilterProxy và OpenEntityManagerInViewFilter). Có nên làm theo cách khác không (Tải mọi thứ bằng ContextLoaderListener và để DispatcherServlet không có cấu hình)?
Ralph

@Ralph: Tốt, tôi đã thêm trường hợp sử dụng đó vào danh sách. Đối với việc rời đi DispatcherServletmà không có cấu hình - nếu bạn làm như vậy, bạn sẽ không có giao diện web. Tất cả những thứ MVC phải vào đó.
skaffman

2
@skaffman Tại sao tôi nên sử dụng hai ngữ cảnh khi sử dụng spring-security với DelegateFilterProxy? Trong trường hợp của tôi, spring-security bean và bối cảnh mùa xuân mặc định có chung một số bean. Vì vậy, họ cũng nên chia sẻ cùng một bối cảnh. Hoặc nên giữ đậu mùa xuân ra khỏi bối cảnh mùa xuân mặc định?
Matthias M

10

Bạn cũng có thể định cấu hình ngữ cảnh ứng dụng theo cách khác. Ví dụ: để làm cho OpenEntityManagerInViewFilter hoạt động. Thiết lập ContextLoaderListener và sau đó định cấu hình DispatcherServlet của bạn với:

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

Chỉ cần đảm bảo rằng giá trị tham số contextConfigLocation trống.


1
Nhưng lợi thế của cấu hình này là gì? Và bạn hiểu "ngược lại" nghĩa là gì?
Ralph

Giải pháp của "skaffman" chỉ định cấu hình ngữ cảnh ứng dụng web (servlet). Tuy nhiên, với cách tiếp cận đó, bạn gặp phải các vấn đề như được nêu chi tiết trong chính giải pháp: "Các lý do thuyết phục duy nhất để sử dụng ngữ cảnh cấp ứng dụng web là:" ... "Nếu bạn có bộ lọc servlet kết nối với ngữ cảnh cấp ứng dụng web (ví dụ: Spring Security's DelegateFilterProxy, OpenEntityManagerInViewFilter, v.v.) "Nếu bạn chỉ muốn sử dụng 1 tệp XML ngữ cảnh ứng dụng, tôi nghĩ giải pháp của tôi (chỉ định XML qua ContextLoaderListener) sẽ phù hợp hơn.
Gunnar Hillert

bạn có thể sử dụng Bộ điều khiển web MVC trong Ngữ cảnh được tạo bởi Trình xử lý ngữ cảnh không?
Ralph

1
Đúng. Bạn chỉ cần thiết lập bộ điều khiển của mình trong tệp context.xml do Trình xử lý ngữ cảnh chỉ định. Cách thức hoạt động là DispatcherServlet sẽ đơn giản tham gia vào "ngữ cảnh ứng dụng mẹ" (Context Listener). Khi bạn để trống giá trị "contextConfigLocation", tệp context.xml do Trình xử lý ngữ cảnh chỉ định sẽ được sử dụng riêng.
Gunnar Hillert

1
Tôi nghĩ rằng bạn đã bỏ lỡ <mvc: annotation-driven /> trong ngữ cảnh của mình. Giải pháp @GunnarHillert phù hợp với tôi.
milbr

10

Tôi muốn chia sẻ những gì tôi đã làm trên ứng dụng Spring-MVC của mình:

  1. Trên, we-mvc-config.xmltôi chỉ thêm các lớp được chú thích bằng @Controller:

    <context:component-scan base-package="com.shunra.vcat">
        <context:include-filter expression="org.springframework.stereotype.Controller" type="annotation"/>
    </context:component-scan>
  2. Trên các applicationContext.xmltệp, tôi đã thêm tất cả phần còn lại:

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

Vâng, đây là một mô hình hữu ích. Một mô hình hữu ích khác là chỉ cần đặt các bean xử lý cơ sở dữ liệu của bạn trong ngữ cảnh ứng dụng (chúng có thể cần thiết cho OpenSessionInViewFilter hoặc tương tự) cùng với bất kỳ thứ gì đặc biệt cần thiết bởi các bộ lọc hoặc trình nghe (ví dụ: các định nghĩa cần thiết để sử dụng bảo mật mùa xuân).
Periata Breatta
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.