在父上下文与子上下文中声明 Spring Bean [英] Declaring Spring Bean in Parent Context vs Child Context

查看:56
本文介绍了在父上下文与子上下文中声明 Spring Bean的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个 spring bean (dao) 对象,我通过以下 xml 在我的 ServletContext 中实例化:

I have a spring bean (dao) object which I instantiate in my ServletContext via the following xml:

<bean id="userDao" class="com.company.dao.impl.UserDaoImpl">
    <property name="sessionFactory" ref="sessionFactory" />
</bean>

此 bean 在我的 webapp-servlet.xml 文件中声明,并由我的应用程序在 ServletContext 中使用.

This bean is declared inside my webapp-servlet.xml file and is used by my app within the ServletContext.

我也在使用 SpringSecurity.我的理解是它运行在不同的上下文中(SecurityContext).

I am also using SpringSecurity. It is my understanding that this runs in a different context (the SecurityContext).

我的应用程序有一个 webapp-security.xml,我在其中实例化了一个自定义身份验证提供程序.我想使用我的应用程序中使用的 dao 在我的安全上下文中进行用户查找,但是当我运行时:

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:

<bean id="userAuthenticationProvider" class="com.company.security.UserAuthenticationProvider">
    <property name="userDao" ref="userDao" />
</bean>

我收到错误消息,说没有这样的 beanuserDao".bean 在我的其他上下文中声明的 bean 中自动装配得很好,但不在我的安全上下文中.根据 Spring Docs,我相信 web.xml 中需要两个单独的上下文

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

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

<listener>
    <listener-class>
        org.springframework.security.web.session.HttpSessionEventPublisher
    </listener-class>
</listener>

所以我的问题是,我如何才能访问位于我的 SecurityContext 中的 ServletContext 中的 DAO?我的 dao 是否有范围修饰符,或者我可以在运行时以某种方式在身份验证提供程序中获取 ServletContext 吗?作为参考,这是我想在我的身份验证提供程序中使用它的方式:

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:

public class UserAuthenticationProvider extends
    AbstractUserDetailsAuthenticationProvider {

    @Override
protected UserDetails retrieveUser(String userName,
        UsernamePasswordAuthenticationToken authenticationToken)
        throws AuthenticationException {

    // use dao here

谢谢你向我解释这个

更新:

继续我的调查,似乎我使用我的 daos 的 DispatcherServlet 是一个子上下文,而安全上下文位于更高的位置.因此,父上下文无法看到我的 DispatcherServlet 中的 bean.我认为答案是以某种方式将我的 bean 声明移动到父应用程序上下文中,但我不确定如何执行此操作.这是我的 web.xml

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

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

    ...

我将我所有的 dao 创建移出到 spring-dao.xml,在我的 spring-security.xml 中,我现在正在做:

I moved all of my dao creation out to a spring-dao.xml, and in my spring-security.xml I am now doing a:

<import resource="spring-dao.xml" />

daos 仍然对 DispatcherServlet 上下文可见,但对我的 SecurityContext 不可见.

The daos stil remain visible to the DispatcherServlet context and invisible to my SecurityContext though.

回答:

好吧,我想通了.以下是一些有用的链接:

Alright, I figured it out. Here were some helpful links:

http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/beans.html#context-create

http://forum.springsource.org/showthread.php?115774-Spring-Security-Custom-UserDetailsS​​ervice-to-use-User-Service-Dao

http://static.springsource.org/spring-security/site/faq.html#faq-method-security-in-web-context

所以问题是我们需要确保 dao 存在于 ApplicationContext(更高版本的 spring 容器)中.为了确保发生这种情况,我将 web.xml 更改为:

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:

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

我认为这将确保启动的第一个上下文加载器将读取我的 dao 配置(并创建我的 dao bean),然后是我的安全配置.由于 dao bean 是以这种方式创建的,因此我删除了 security.xml 中之前的import resource="spring-dao.xml""语句,因为它将不再需要.

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.

在上下文参数配置之后,我创建了 ContextLoaderListener.这是一个比 DispatcherServlet 更高级别的 spring 容器,所以我认为把它放在最前面会是第一个读取这些配置文件的人,然后会创建 bean.然后,任何子上下文都可以访问它们.这可能不是它的工作方式,因为 DispatcherServlet 甚至可能不会读取 contextConfigLocation,但即使读取,我认为此时 bean 已经被声明,太糟糕了,父上下文拥有它们.

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 he would 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.

现在,另一个技巧......为了获得我的 DAO,我可以@Autowired 它.我不得不通过 XML 手动注入它:

Now, for another trick... in order to get my DAO, I could not @Autowired it. I had to manually inject it via XML:

    <bean id="userAuthenticationProvider" class="com.company.app.security.UserAuthenticationProvider">
    <property name="userDao" ref="userDao" />
</bean>

当然,我在我的 dao 上创建了 getter 和 setter 方法,瞧!我不知道为什么 @Autowired 在这里不起作用.我认为这是设计使然.也许这是 SecurityContext 特有的(它不会从其他上下文中提取),或者@Autowired 通常从当前上下文中提取,或者可能因为我通过 XML 创建了 bean,我必须还通过 xml 而不是通过注释设置任何属性?(注解已启用并在我的顶级应用程序命名空间中工作).

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 only pulls 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).

无论如何..还是有很多我不明白,但重要的是它终于可以工作了.

Anyways.. still a lot I don't understand, but the important point is it's finally working.

推荐答案

如果你打算使用 Spring MVC,你肯定需要 了解Spring MVC的ApplicationContext层次结构.您还应该了解 servlet 容器中的基本组件和生命周期,因为您似乎也对侦听器和 servlet 的工作方式感到困惑.

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.

简要说明您的情况:

  1. 您正在创建两个 ApplicationContext:根上下文和 DispatcherServlet 上下文.根上下文由 ContextLoaderListener 根据在 contextConfigLocation 中命名的文件创建.此上下文旨在包含构成应用程序核心逻辑的 bean.DispatcherServlet 上下文在该 servlet 启动时创建,并基于名为webapp-servlet.xml"的文件.此上下文旨在包含支持与其关联的 DispatcherServlet 实例的所有 bean,并且其中应仅包含与视图相关的 bean.
  2. DispatcherServlet 上下文成为根上下文的子级.这允许将根上下文中的核心 bean 注入到视图层 bean 中.可见性是一种方式.视图层 bean 不可用于核心 bean,这是可取的.这就是为什么您的 DAO 无法注入您的身份验证提供程序的原因.DAO 在子上下文中.
  3. 基于注解的服务仅适用于声明它们的上下文.如果@Autowired 不适用于特定 bean,那是因为您尚未声明 在该 bean 所在的上下文中.

这篇关于在父上下文与子上下文中声明 Spring Bean的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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