Java spring web, security + web.xml + mvc dispatcher + Bean 创建两次

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19826228/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 20:31:47  来源:igfitidea点击:

spring web, security + web.xml + mvc dispatcher + Bean is created twice

javaspringspring-mvcspring-security

提问by Jaxox

I have the Web.xml as below:

我有 Web.xml 如下:

 <servlet>
    <servlet-name>mvc-dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>mvc-dispatcher</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>
<filter>
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/api/secure/*</url-pattern>
</filter-mapping>

[Edit]

[编辑]

After I added the spring security, then I get the error!

在我添加了 spring 安全之后,我得到了错误!

java.lang.IllegalStateException: No WebApplicationContext found: no ContextLoaderListener registered?

java.lang.IllegalStateException:未找到 WebApplicationContext:未注册 ContextLoaderListener?

then I added

然后我加了

 <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            /WEB-INF/mvc-dispatcher-servlet.xml
        </param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

then it seems working fine, but then 1) The problem is the bean are created twice!

那么它似乎工作正常,但是 1) 问题是 bean 被创建了两次!

if I only remove that:

如果我只删除它:

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>
        /WEB-INF/mvc-dispatcher-servlet.xml
    </param-value>
</context-param>

but leave the <listener>then the web application doesn't run at all

但离开<listener>然后网络应用程序根本不运行

[Extra]

[额外的]

The full Web.xml is below:

完整的 Web.xml 如下:

<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

    <display-name>Spring MVC Application</display-name>

    <servlet>
        <servlet-name>mvc-dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>mvc-dispatcher</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <filter>
        <filter-name>springSecurityFilterChain</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>springSecurityFilterChain</filter-name>
        <url-pattern>/api/secure/*</url-pattern>
    </filter-mapping>

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            /WEB-INF/mvc-dispatcher-servlet.xml
        </param-value>
    </context-param>

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

</web-app>

here is my mvc-dispatcher-servlet.xml

这是我的 mvc-dispatcher-servlet.xml

<beans xmlns="http://www.springframework.org/schema/beans" 
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:jpa="http://www.springframework.org/schema/data/jpa"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:security="http://www.springframework.org/schema/security"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
                            http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
                            http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd
                            http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
                            http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd">

    <mvc:annotation-driven/>
    <context:annotation-config/>
    <context:component-scan base-package="com.ge.wtracker"/>
    <context:property-placeholder location="classpath*:META-INF/spring/*.properties"/>

    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/pages/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

    <!--Java Persistence API config-->
    <jpa:repositories base-package="com.ge.wtracker.repository"/>

    <!--JPA and Database Config-->
    <tx:annotation-driven transaction-manager="transactionManager"/>
    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="persistenceUnitName" value="defaultPersistenceUnit"/>
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory" />
    </bean>
    <bean class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" id="dataSource">
        <property name="driverClassName" value="${database.driverClassName}"/>
        <property name="url" value="${database.url}"/>
        <property name="username" value="${database.username}"/>
        <property name="password" value="${database.password}"/>
        <property name="testOnBorrow" value="true"/>
        <property name="testOnReturn" value="true"/>
        <property name="testWhileIdle" value="true"/>
        <property name="timeBetweenEvictionRunsMillis" value="1800000"/>
        <property name="numTestsPerEvictionRun" value="3"/>
        <property name="minEvictableIdleTimeMillis" value="1800000"/>
        <property name="validationQuery" value="SELECT 1"/>
    </bean>

    <!--Spring Security-->
    <security:http create-session="stateless" entry-point-ref="restAuthenticationEntryPoint" authentication-manager-ref="authenticationManager">
        <security:intercept-url pattern="/api/secure/**" access="ROLE_USER" />
        <security:custom-filter ref="customRestFilter" position="BASIC_AUTH_FILTER" />
    </security:http>
    <!-- Configures the authentication entry point that returns HTTP status code 401 -->
    <bean id="restAuthenticationEntryPoint" class="com.ge.wtracker.web.security.RestAuthenticationEntryPoint">
        <property name="realmName" value="Not Authorized" />
    </bean>
    <security:authentication-manager alias="authenticationManager">
        <security:authentication-provider ref="restAuthenticationProvider" />
    </security:authentication-manager>

    <!--The customRestFilter responsibles for retrieving and manipulating any request data to pass to the authentication
        provider to authenticate.-->
    <bean id="customRestFilter" class="com.ge.wtracker.web.security.RestSecurityFilter">
        <constructor-arg name="authenticationManager" ref="authenticationManager" />
        <constructor-arg name="authenticationEntryPoint" ref="restAuthenticationEntryPoint" />
    </bean>
    <bean id="restAuthenticationProvider" class="com.ge.wtracker.web.security.RestAuthenticationProvider" />

</beans>

回答by samlewis

http://docs.spring.io/spring/docs/3.0.0.M3/reference/html/ch16s02.html

http://docs.spring.io/spring/docs/3.0.0.M3/reference/html/ch16s02.html

The framework will, on initialization of a DispatcherServlet, look for a file named [servlet-name]-servlet.xml in the WEB-INF directory of your web application and create the beans defined there (overriding the definitions of any beans defined with the same name in the global scope).

在初始化 DispatcherServlet 时,框架将在 Web 应用程序的 WEB-INF 目录中查找名为 [servlet-name]-servlet.xml 的文件,并创建在那里定义的 bean(覆盖使用全局范围内的相同名称)。

So you can remove the context-param:

所以你可以删除上下文参数:

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>
        /WEB-INF/mvc-dispatcher-servlet.xml
    </param-value>
</context-param>

回答by Sotirios Delimanolis

During the servlet container lifecycle, the container first initializes the ServletContextListener, then the Filterand Servletinstances.

在 servlet 容器生命周期中,容器首先初始化ServletContextListener,然后是FilterServlet实例。

A Spring Web application typically loads two contexts: the root context and the dispatcher servlet context. The ContextLoaderListenerclass is a ServletContextListenerwhich loads the application (or root) context. It identifies the file to load either through the context-paramwith the name contextConfigLocationas given in the web.xml like below

Spring Web 应用程序通常加载两个上下文:根上下文和调度程序 servlet 上下文。的ContextLoaderListener类是ServletContextListener它加载应用程序(或根)上下文。它标识要通过web.xml 中给出context-param的名称加载的文件,contextConfigLocation如下所示

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>
        /WEB-INF/mvc-dispatcher-servlet.xml
    </param-value>
</context-param>

or, by default, by looking for a file at /WEB-INF/applicationContext.xml. Since you've specified /WEB-INF/mvc-dispatcher-servlet.xmlas the contextConfigLocation, that context will be loaded.

或者,默认情况下,通过在/WEB-INF/applicationContext.xml. 由于您已指定/WEB-INF/mvc-dispatcher-servlet.xmlcontextConfigLocation,因此将加载该上下文。

Once this is done, the container initializes the DispatcherServlet, which also loads a context. It identifies the file load either through an init-paramelement with the name contextConfigLocationas given in the web.xml below

完成此操作后,容器会初始化DispatcherServlet,这也会加载上下文。它通过具有以下 web.xml 中给出init-param的名称的元素来标识文件加载contextConfigLocation

<servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/spring/appServlet/some-random-location.xml</param-value>
    </init-param>
</servlet>

or, by default, by looking for a file at /WEB-INF/name-of-your-servlet-servlet.xml. In other words, it takes the value of the <servlet-name>element and appends -servlet.xmlto it and looks for it in WEB-INF.

或者,默认情况下,通过在/WEB-INF/name-of-your-servlet-servlet.xml. 换句话说,它获取<servlet-name>元素的值并附-servlet.xml加到它并在 中查找它WEB-INF

Since you haven't specified an init-paramwith name contextConfigLocation, the DispatcherServletloads the context file at /WEB-INF/mvc-dispatcher-servlet.xml, since its name is mvc-dispatcher. The context loaded by the DispatcherServlethas access to the beans loaded by the ContextLoaderListener, that's why we call that the root context (and the others children).

由于您尚未指定init-paramwith name contextConfigLocation,因此DispatcherServlet会在 处加载上下文文件/WEB-INF/mvc-dispatcher-servlet.xml,因为它的名称是mvc-dispatcher. 加载的上下文DispatcherServlet可以访问 加载的 bean ContextLoaderListener,这就是我们称其为根上下文(以及其他子上下文)的原因。

All this to say that both your ContextLoaderListenerand your DispatcherServletare creating their own copy of an ApplicationContextby each loading a XmlWebApplicationContextfrom the same file at /WEB-INF/mvc-dispatcher-servlet.xml.

所有这一切都是为了说明您ContextLoaderListener和您DispatcherServlet正在创建自己的 an 副本,方法ApplicationContextXmlWebApplicationContext/WEB-INF/mvc-dispatcher-servlet.xml.

Identify what beans or configuration you think should be available to the whole application and put them in the file that will be loaded by the ContextLoaderListener. Identify the beans or configuration you think should be available to the DispatcherServletand put them in its context file.

确定您认为应该对整个应用程序可用的 bean 或配置,并将它们放在将由ContextLoaderListener. 确定您认为应该可用的 bean 或配置DispatcherServlet,并将它们放入其上下文文件中。

回答by ashish

Spring MVC then create a new any-name.xml and place context specific beans like Spring-security.xml and for your security bean to load.

Spring MVC 然后创建一个新的 any-name.xml 并放置上下文特定的 bean,如 Spring-security.xml 并为您的安全 bean 加载。

New xml will be like this

新的xml会是这样

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/beans     
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context 
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task-3.1.xsd">

 <mvc:annotation-driven/>
  <context:component-scan base-package="com"/>
<task:annotation-driven/> 
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.
config.PropertyPlaceholderConfigurer">
 <property name="location" value=""/>
    <property name="locations">
           <list>
               <value>/WEB-INF/jdbc.properties</value>
           </list>
        </property>
</bean>
<bean id="dataSource"
    class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="${jdbc.driverClassName}"/>
        <property name="url" value="${jdbc.databaseurl}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>            
</bean>
</beans>

and now include this in web.xml

现在将其包含在 web.xml 中

  <context-param>
   <param-name>contextConfigLocation</param-name>
    <param-value>  
      /WEB-INF/login-security.xml,
      /WEB-INF/application-context.xml
   </param-value>
 </context-param>

hope this will help you.

希望这会帮助你。