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
spring web, security + web.xml + mvc dispatcher + Bean is created twice
提问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 Filter
and Servlet
instances.
在 servlet 容器生命周期中,容器首先初始化ServletContextListener
,然后是Filter
和Servlet
实例。
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
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.xml
as the contextConfigLocation
, that context will be loaded.
或者,默认情况下,通过在/WEB-INF/applicationContext.xml
. 由于您已指定/WEB-INF/mvc-dispatcher-servlet.xml
为contextConfigLocation
,因此将加载该上下文。
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
完成此操作后,容器会初始化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.xml
to 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-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).
由于您尚未指定init-param
with name contextConfigLocation
,因此DispatcherServlet
会在 处加载上下文文件/WEB-INF/mvc-dispatcher-servlet.xml
,因为它的名称是mvc-dispatcher
. 加载的上下文DispatcherServlet
可以访问 加载的 bean ContextLoaderListener
,这就是我们称其为根上下文(以及其他子上下文)的原因。
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
.
所有这一切都是为了说明您ContextLoaderListener
和您DispatcherServlet
正在创建自己的 an 副本,方法ApplicationContext
是XmlWebApplicationContext
从/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 DispatcherServlet
and 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.
希望这会帮助你。