一尘不染

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

spring

我有一个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

感谢你向我解释

更新:

继续我的调查,似乎我在使用dao的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不可见。

因此,问题在于我们需要确保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.

在配置上下文参数之后,我立即创建了ContextLoaderListener。这是一个比DispatcherServlet更高级的spring容器,因此我认为将其放在第一位是第一个读取这些配置文件的人,然后他将创建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和不是通过注释?(注释已启用,并且可以在我的顶级应用程序名称空间中使用)。

无论如何..我仍然不了解很多,但是重要的是它终于可以工作了。


阅读 501

收藏
2020-04-12

共1个答案

一尘不染

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

简要说明你的情况:

  1. 你正在创建两个ApplicationContext:根上下文和DispatcherServlet上下文。根上下文是由ContextLoaderListener基于contextConfigLocation中命名的文件创建的。该上下文旨在包含构成应用程序核心逻辑的bean。当该Servlet启动时将创建DispatcherServlet上下文,该上下文基于名为“ webapp-servlet.xml”的文件。该上下文旨在包含所有支持与其关联的DispatcherServlet实例的bean,并且其中仅应包含与视图相关的bean。
  2. DispatcherServlet上下文成为根上下文的子级。这样就可以将根上下文中的核心bean注入到视图层bean中。可见性是单向的。视图层bean对核心bean不可用,这是理想的。这就是为什么你的DAO无法注入到身份验证提供程序中的原因。DAO是在子上下文中。
  3. 基于注释的服务仅适用于声明它们的上下文。如果@Autowired不适用于特定的bean,那是因为你尚未声明该bean <context:component-scan/><context:annotation-config/>在该bean存在的上下文中。
2020-04-12