在父上下文和子上下文中声明Spring Bean

时间:2011-10-12 21:10:04

标签: security spring ioc-container servlets security-context

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

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

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

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

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

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

我收到错误,说没有这样的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>

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

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

<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中,我现在正在执行:

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

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

ANSWERED:

好吧,我明白了。以下是一些有用的链接:

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-UserDetailsService-to-use-User-Service-Dao

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

所以问题是我们需要确保dao存在于ApplicationContext中(更高的弹簧容器)。为确保发生这种情况,我将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>

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

在context-param配置之后,我创建了ContextLoaderListener。这是一个比DispatcherServlet更高级的弹簧容器,所以我认为首先是第一个读取这些配置文件的人,然后将创建bean。然后,任何子上下文都可以访问它们。这可能不是它的工作方式,因为DispatcherServlet甚至可能都没有读取contextConfigLocation,但即使它确实如此,我认为此时bean已经被声明,所以太糟糕了,父上下文拥有它们。

现在,换另一个技巧......为了得到我的DAO,我可以 @Autowired它。我不得不通过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而不是通过注释设置任何属性? (注释已启用并在我的顶级应用程序命名空间中工作)。

无论如何..还有很多我不明白,但重要的是它终于有效了。

1 个答案:

答案 0 :(得分:60)

如果你打算使用Spring MVC,你肯定需要understand Spring MVC's ApplicationContext hierarchy。您还应该了解basic components and lifecycles in a servlet container,因为您似乎对侦听器和servlet的工作原理感到困惑。

简要解释一下你的情况:

  1. 您正在创建两个ApplicationContexts:根上下文和DispatcherServlet上下文。根上下文由ContextLoaderListener基于contextConfigLocation中指定的文件创建。此上下文旨在包含组成应用程序核心逻辑的bean。 DispatcherServlet上下文是在该servlet启动时创建的,并且基于名为“webapp-servlet.xml”的文件。此上下文旨在包含任何支持与其关联的DispatcherServlet实例的bean,并且应该只包含与视图相关的bean。
  2. DispatcherServlet上下文成为根上下文的子级。这允许将根上下文中的核心bean注入视图层bean。可见性是单向的。视图层bean不可用于核心bean,这是可取的。这就是您的DAO无法注入您的身份验证提供程序的原因。 DAO处于子环境中。
  3. 基于注释的服务仅适用于声明它们的上下文。如果@Autowired不适用于特定的bean,那是因为你没有在该bean存在的上下文中声明<context:component-scan/><context:annotation-config/>