在父上下文与子上下文中声明 Spring Bean
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/7746633/
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
Declaring Spring Bean in Parent Context vs Child Context
提问by dev
I have a spring bean (dao) object which I instantiate in my ServletContext via the following xml:
我有一个 spring bean (dao) 对象,我通过以下 xml 在我的 ServletContext 中实例化它:
<bean id="userDao" class="com.company.dao.impl.UserDaoImpl">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
This bean is declared inside my webapp-servlet.xml file and is used by my app within the ServletContext.
这个 bean 在我的 webapp-servlet.xml 文件中声明,并被我的应用程序在 ServletContext 中使用。
I am also using SpringSecurity. It is my understanding that this runs in a different context (the SecurityContext).
我也在使用 SpringSecurity。我的理解是它在不同的上下文(SecurityContext)中运行。
My application has a webapp-security.xml where I instantiate a custom authentication provider. I would like to use my dao that is used in my app to also do the user lookup in my security context, but when I run:
我的应用程序有一个 webapp-security.xml,我在其中实例化了一个自定义身份验证提供程序。我想使用我的应用程序中使用的 dao 来在我的安全上下文中进行用户查找,但是当我运行时:
<bean id="userAuthenticationProvider" class="com.company.security.UserAuthenticationProvider">
<property name="userDao" ref="userDao" />
</bean>
I get errors saying that there is no such bean "userDao". The bean is autowired fine in beans declared in my other context, but not inside my security context. According to the Spring Docs, I believe both separate contexts are needed in web.xml
我收到错误消息,说没有这样的 bean“userDao”。bean 在我的其他上下文中声明的 bean 中自动装配得很好,但不在我的安全上下文中。根据 Spring Docs,我相信 web.xml 中需要两个单独的上下文
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<listener>
<listener-class>
org.springframework.security.web.session.HttpSessionEventPublisher
</listener-class>
</listener>
So my question is, how can I get access to my DAO that lives in my ServletContext inside my SecurityContext? Is there a scope modifier for my dao, or could I somehow get the ServletContext at runtime within my authentication provider? For reference, this is how I want to use it inside my authentication provider:
所以我的问题是,我如何才能访问位于我的 SecurityContext 内的 ServletContext 中的 DAO?我的 dao 是否有范围修饰符,或者我可以在运行时以某种方式在身份验证提供程序中获取 ServletContext 吗?作为参考,这是我想在我的身份验证提供程序中使用它的方式:
public class UserAuthenticationProvider extends
AbstractUserDetailsAuthenticationProvider {
@Override
protected UserDetails retrieveUser(String userName,
UsernamePasswordAuthenticationToken authenticationToken)
throws AuthenticationException {
// use dao here
thanks for explaining this to me
谢谢你向我解释这个
UPDATE:
更新:
Continuing my investigation, it seems that the DispatcherServlet where I'm using my daos is a child context, and the security context is somewhere higher up. Consequently, beans in my DispatcherServlet can not be seen by parent contexts. I think the answer is to move my bean declarations into the parent application context somehow, but i'm not sure how to do this. Here is my web.xml
继续我的调查,似乎我使用 daos 的 DispatcherServlet 是一个子上下文,而安全上下文位于更高的位置。因此,父上下文无法看到我的 DispatcherServlet 中的 bean。我认为答案是以某种方式将我的 bean 声明移动到父应用程序上下文中,但我不确定如何执行此操作。这是我的 web.xml
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>WEB-INF/spring-*.xml
</param-value>
</context-param>
<listener>
<listener-class>
org.springframework.security.web.session.HttpSessionEventPublisher
</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<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>/*</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>myapp</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
<init-param>
<param-name>listings</param-name>
<param-value>true</param-value>
</init-param>
</servlet>
...
I moved all of my dao creation out to a spring-dao.xml, and in my spring-security.xml I am now doing a:
我将我所有的 dao 创建移到了 spring-dao.xml,在我的 spring-security.xml 中,我现在正在做:
<import resource="spring-dao.xml" />
The daos stil remain visible to the DispatcherServlet context and invisible to my SecurityContext though.
daos 仍然对 DispatcherServlet 上下文可见,但对我的 SecurityContext 不可见。
ANSWERED:
回答:
Alright, I figured it out. Here were some helpful links:
好吧,我想通了。以下是一些有用的链接:
http://static.springsource.org/spring-security/site/faq.html#faq-method-security-in-web-context
http://static.springsource.org/spring-security/site/faq.html#faq-method-security-in-web-context
So the problem was we need to make sure that the dao exists in the ApplicationContext (higher up spring container). To make sure this happened I changed my web.xml to be:
所以问题是我们需要确保 dao 存在于 ApplicationContext(更高层的 spring 容器)中。为了确保发生这种情况,我将 web.xml 更改为:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>WEB-INF/spring-dao.xml WEB-INF/spring-security.xml
</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<listener>
<listener-class>
org.springframework.security.web.session.HttpSessionEventPublisher
</listener-class>
</listener>
<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>/*</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>webapp</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
<init-param>
<param-name>listings</param-name>
<param-value>true</param-value>
</init-param>
</servlet>
I thought this would make sure that the first context loader that starts up will read my dao config (and create my dao beans), then my security config. Since the dao beans are being created this way, I removed the previous "import resource="spring-dao.xml"" statement in the security.xml because it will no longer be needed.
我认为这将确保启动的第一个上下文加载器将读取我的 dao 配置(并创建我的 dao bean),然后是我的安全配置。由于 dao bean 是以这种方式创建的,因此我删除了 security.xml 中先前的“import resource="spring-dao.xml"”语句,因为它将不再需要。
Right after that context-param configuration I created the ContextLoaderListener. This is a higher-level spring container than the DispatcherServlet, so I figured putting this first would be the first guy to read those config files, and hewould then create the beans. Then, any child-context would have access to them. This may not be how it works as DispatcherServlet may not even read the contextConfigLocation, but even if it does, I figured that at this point the beans would already be declared, so too bad, the parent context owns them.
在上下文参数配置之后,我创建了 ContextLoaderListener。这是一个比 DispatcherServlet 更高级别的 spring 容器,所以我认为把它放在最前面会是第一个读取这些配置文件的人,然后他会创建 bean。然后,任何子上下文都可以访问它们。这可能不是它的工作方式,因为 DispatcherServlet 甚至可能不会读取 contextConfigLocation,但即使读取,我认为此时 bean 已经被声明,太糟糕了,父上下文拥有它们。
Now, for another trick... in order to get my DAO, I could not@Autowired it. I had to manually inject it via XML:
现在,另一个技巧......为了获得我的 DAO,我无法@Autowired 它。我不得不通过 XML 手动注入它:
<bean id="userAuthenticationProvider" class="com.company.app.security.UserAuthenticationProvider">
<property name="userDao" ref="userDao" />
</bean>
Of course, I made the getter and setter methods on my dao, and voila! I do not know why the @Autowired does not work here. I assume it's by design. Perhaps this is particular to the SecurityContext (it will not pull from other contexts), or perhaps @Autowired in general onlypulls from the current context, or maybe because I created the bean via XML, I have to also set any properties via xml and not via annotations? (annotations are enabled and working in my top-level application namespace).
当然,我在我的 dao 上创建了 getter 和 setter 方法,瞧!我不知道为什么 @Autowired 在这里不起作用。我认为这是设计使然。也许这是 SecurityContext 特有的(它不会从其他上下文中提取),或者@Autowired 通常只从当前上下文中提取,或者可能因为我通过 XML 创建了 bean,我还必须通过 xml 和不是通过注释?(注释已启用并在我的顶级应用程序命名空间中工作)。
Anyways.. still a lot I don't understand, but the important point is it's finally working.
无论如何..仍然有很多我不明白,但重要的是它终于起作用了。
回答by Ryan Stewart
If you're going to use Spring MVC, you definitely need to understand Spring MVC's ApplicationContext hierarchy. You should also learn something about the basic components and lifecycles in a servlet container, since you seem to be confused about how listeners and servlets work, too.
如果您打算使用 Spring MVC,您肯定需要了解 Spring MVC 的 ApplicationContext 层次结构。您还应该了解servlet 容器中的基本组件和生命周期,因为您似乎也对侦听器和 servlet 的工作方式感到困惑。
To explain your situation briefly:
简要说明您的情况:
- You're creating two ApplicationContexts: the root context and the DispatcherServlet context. The root context is created by the ContextLoaderListener based on the files named in the contextConfigLocation. This context is intended to contain the beans that compose the core logic of your app. The DispatcherServlet context is created when that servlet starts and is based on the file named "webapp-servlet.xml". This context is intended to contain any beans that support the DispatcherServlet instance that it's associated with and should only have view-related beans in it.
- The DispatcherServlet context becomes a child of the root context. That allows your core beans from the root context to be injected into the view-layer beans. Visibility is one-way. The view-layer beans aren't available to the core beans, which is desirable. This is why your DAO couldn't be injected into your authentication provider. The DAO was in the child context.
- Annotation-based services only apply within the context where they're declared. If @Autowired isn't working for a particular bean, it's because you haven't declared
<context:component-scan/>or<context:annotation-config/>in the context where that bean exists.
- 您正在创建两个 ApplicationContext:根上下文和 DispatcherServlet 上下文。根上下文由 ContextLoaderListener 根据在 contextConfigLocation 中命名的文件创建。此上下文旨在包含构成应用程序核心逻辑的 bean。DispatcherServlet 上下文在该 servlet 启动时创建,并基于名为“webapp-servlet.xml”的文件。此上下文旨在包含支持与其关联的 DispatcherServlet 实例的任何 bean,并且其中应仅包含与视图相关的 bean。
- DispatcherServlet 上下文成为根上下文的子级。这允许将根上下文中的核心 bean 注入到视图层 bean 中。可见性是一种方式。视图层 bean 不可用于核心 bean,这是可取的。这就是无法将您的 DAO 注入您的身份验证提供程序的原因。DAO 在子上下文中。
- 基于注解的服务仅适用于它们被声明的上下文。如果 @Autowired 不适用于特定 bean,那是因为您尚未声明
<context:component-scan/>或<context:annotation-config/>在该 bean 存在的上下文中。

