spring web, security + web.xml + mvc dispatcher + Bean 创建两次 [英] spring web, security + web.xml + mvc dispatcher + Bean is created twice

查看:23
本文介绍了spring web, security + web.xml + mvc dispatcher + Bean 创建两次的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的 Web.xml 如下:

I have the Web.xml as below:

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

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

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

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

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

然后我加了

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

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

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

如果我只删除它:

<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

[额外]

完整的 Web.xml 如下:

The full Web.xml is below:

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

这是我的 mvc-dispatcher-servlet.xml

here is my 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>

推荐答案

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

During the servlet container lifecycle, the container first initializes the ServletContextListener, then the Filter and Servlet instances.

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

A Spring Web application typically loads two contexts: the root context and the dispatcher servlet context. The ContextLoaderListener class is a ServletContextListener which loads the application (or root) context. It identifies the file to load either through the context-param with the name contextConfigLocation as given in the web.xml like below

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

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

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

一旦完成,容器将初始化DispatcherServlet,它也加载一个上下文.它通过一个名为 contextConfigLocationinit-param 元素来标识文件加载,如下面的 web.xml 中给出的

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

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

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

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.xml to it and looks for it in WEB-INF.

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

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

所有这些都是为了说明您的 ContextLoaderListener 和您的 DispatcherServlet 都在通过每个加载一个 来创建自己的 ApplicationContext 副本XmlWebApplicationContext 来自 /WEB-INF/mvc-dispatcher-servlet.xml 的同一文件.

All this to say that both your ContextLoaderListener and your DispatcherServlet are creating their own copy of an ApplicationContext by each loading a XmlWebApplicationContext from the same file at /WEB-INF/mvc-dispatcher-servlet.xml.

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

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 DispatcherServlet and put them in its context file.

这篇关于spring web, security + web.xml + mvc dispatcher + Bean 创建两次的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆