ContextLoaderListener ou não?


122

Um aplicativo web spring padrão (criado pelo Roo ou modelo "Spring MVC Project") cria um web.xml com ContextLoaderListenere DispatcherServlet. Por que eles não apenas usam DispatcherServlete fazem o carregamento da configuração completa?

Entendo que o ContextLoaderListener deve ser usado para carregar o material que não é relevante para a Web e o DispatcherServlet é usado para carregar o material relevante para a Web (Controladores, ...). E isso resulta em dois contextos: um pai e um filho.

Fundo:

Eu estava fazendo isso dessa maneira padrão por vários anos.

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

Isso geralmente causava problemas nos dois contextos e nas dependências entre eles. No passado, sempre fui capaz de encontrar uma solução e tenho a forte sensação de que isso torna a estrutura / arquitetura do software sempre melhor. Mas agora estou enfrentando um problema com os eventos dos dois contextos .

- No entanto, isso faz com que eu repense esse padrão de dois contextos, e estou me perguntando: por que devo me meter nesse problema, por que não carregar todos os arquivos de configuração de primavera com um DispatcherServlete remover ContextLoaderListenercompletamente. (Ainda terei arquivos de configuração diferentes, mas apenas um contexto.)

Existe algum motivo para não remover o ContextLoaderListener?


"Isso geralmente causava problemas nos dois contextos e nas dependências entre eles". Este é um ótimo exemplo de como acho que as estruturas de injeção de dependência apenas tornam nossas vidas mais difíceis do que a injeção de dependência do tipo faça você mesmo.
217 Andy

1
@ Andy - Embora eu tenha alguma simpatia com esse ponto de vista, não posso deixar de notar que os casos de uso para os quais você precisa dos dois contextos (compartilhamento de objetos entre filtros de segurança e servlets, gerenciamento automático de transações para que sejam fechadas após a exibição) para o qual você redirecionou a renderização concluída) são bastante difíceis de obter sem a ajuda da estrutura. Isso ocorre principalmente porque a API do servlet claramente nunca foi projetada para funcionar com injeção de dependência e funciona ativamente contra você se você tentar fazer isso sozinho.
Periata Breatta

@PeriataBreatta I see! Bem, você acha que se tivesse sido projetado de maneira diferente, haveria alternativas melhores para o Spring MVC? Embora as pessoas poderiam ter concebido alternativas completas à API Servlet de qualquer maneira ...
Andy

@PeriataBreatta É interessante notar que no mundo JS, onde uso o Express para rotear solicitações HTTP há cerca de um ano, raramente vejo menção a "injeção de dependência" e nada parecido com o framework Spring.
Andy

Respostas:


86

No seu caso, não, não há razão para manter o ContextLoaderListenere applicationContext.xml. Se o seu aplicativo funciona bem apenas com o contexto do servlet, que permanece com isso, é mais simples.

Sim, o padrão geralmente incentivado é manter coisas que não são da Web no contexto de aplicativos da Web, mas não passa de uma convenção fraca.

Os únicos motivos convincentes para usar o contexto no nível de aplicativo da web são:

  • Se você tiver vários DispatcherServletque precisam compartilhar serviços
  • Se você tiver servlets herdados / não Spring que precisem acessar serviços com fio Spring
  • Se você tem filtros de servlet esse gancho para o contexto de nível webapp (por exemplo, do Spring Security DelegatingFilterProxy, OpenEntityManagerInViewFilteretc.)

Nada disso se aplica a você, portanto a complexidade extra não é garantida.

Apenas tenha cuidado ao adicionar tarefas em segundo plano ao contexto do servlet, como tarefas agendadas, conexões JMS, etc. Se você esquecer de adicionar <load-on-startup>ao seu web.xml, essas tarefas não serão iniciadas até o primeiro acesso do servlet.


2
E os ouvintes, parece que eles precisam da criação de contexto pelo ouvinte do Context Loader (IllegalStateException, nenhum WebApplicationContext encontrado, acionado por MultipartFilter, CharacterEncodingFilter, HiddenHttpMethodFilter, Spring Security DelegatingFilterProxy e OpenEntityManagerInViewFilter). É uma boa idéia fazer o contrário (carregar tudo pelo ContextLoaderListener e deixar o DispatcherServlet sem uma configuração)?
Ralph

@ Ralph: Boa captura, eu adicionei esse caso de uso à lista. Quanto a sair DispatcherServletsem uma configuração - se você fizesse isso, não teria interface web. Todo o material MVC tem que entrar lá.
skaffman

2
@skaffman Por que devo usar dois contextos ao usar o spring-security com o DelegatingFilterProxy? No meu caso, os beans de segurança de primavera e o contexto padrão de primavera compartilham alguns beans. Então eles também devem compartilhar o mesmo contexto. Ou os beans de segurança da primavera devem ficar fora do contexto padrão da primavera?
Matthias M

10

Você também pode configurar o contexto do aplicativo ao contrário. Por exemplo, para fazer o OpenEntityManagerInViewFilter funcionar. Configure o ContextLoaderListener e, em seguida, configure seu DispatcherServlet com:

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

Apenas verifique se o valor do parâmetro contextConfigLocation está vazio.


1
Mas qual é a vantagem dessa configuração? E o que você quer dizer com "o contrário"?
28813 Ralph

A solução de "skaffman" configurou apenas um contexto de aplicativo da web (servlet). No entanto, com essa abordagem, você se depara com problemas detalhados na própria solução: "Os únicos motivos convincentes para usar o contexto no nível do aplicativo da web são:" ... "Se você tiver filtros de servlet que se conectam ao contexto no nível do aplicativo da web (por exemplo, DelegatingFilterProxy da Spring Security, OpenEntityManagerInViewFilter, etc) "Se você quiser usar apenas um arquivo XML de contexto de aplicativo, acho que minha solução (especificar o XML por meio do ContextLoaderListener) seria preferível.
Gunnar Hillert

você é capaz de usar o MVC Web Controller no contexto criado pelo ouvinte de contexto?
Ralph

1
Sim. Você simplesmente configuraria seus controladores no arquivo context.xml especificado pelo Context Listener. A maneira como funciona é que o DispatcherServlet simplesmente ingressará no "contexto do aplicativo pai" (Context Listener). Quando você deixar o valor "contextConfigLocation" vazio, o arquivo context.xml especificado pelo Context Listener será usado exclusivamente.
Gunnar Hillert

1
Eu acho que você perdeu <mvc: acionado por anotação /> no seu contexto. A solução @GunnarHillert funciona para mim.
milbr

10

Quero compartilhar o que fiz no meu aplicativo Spring-MVC:

  1. No we-mvc-config.xmleu adicionei apenas as classes anotadas com @Controller:

    <context:component-scan base-package="com.shunra.vcat">
        <context:include-filter expression="org.springframework.stereotype.Controller" type="annotation"/>
    </context:component-scan>
  2. Nos applicationContext.xmlarquivos, adicionei todo o resto:

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

Sim, este é um padrão útil. Outro padrão útil é apenas colocar seus beans de manipulação de banco de dados no contexto do aplicativo (provavelmente são necessários para um OpenSessionInViewFilter ou similar) junto com qualquer coisa especificamente necessária para filtros ou ouvintes (por exemplo, definições necessárias para usar a segurança da primavera).
Periata Breatta
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.