当前位置: 首页 > 面试题库 >

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

赫连坚
2023-03-14
问题内容

我有一个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和不是通过注释?(注释已启用,并且可以在我的顶级应用程序名称空间中使用)。

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


问题答案:

如果要使用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存在的上下文中。


 类似资料:
  • 我有一个使用子/父上下文关系的Spring应用程序。这样做的原因是为了确保子上下文从父上下文继承bean/资源,然后根据需要添加更多bean/资源来覆盖它们。但是,当子上下文关闭时(退出try/catch作用域),它开始对它引用的所有bean进行清理,包括父作用域中的bean。这是不可取的,因为我需要重用父上下文来创建另一个子上下文,但是现在它是垃圾,因为它包含了一堆已处理/关闭的bean。 问题

  • 问题内容: 我在尝试使用定义上下文层次结构时遇到问题。 问题是在内部定义模块上下文并使用另一个上下文(基于XML / Annotated)设置’parent’属性时。 例: 模块A中的beanRefContext.xml db-context.xml 模块B中的beanRefContext.xml 冬眠道 模块B应用程序上下文无法找到在模块A应用程序上下文中定义的bean。 从代码看,扫描过程似乎

  • 问题内容: 我正在尝试在Spring上下文文件中创建对象数组,以便可以将其注入声明为这样的构造函数中: 我正在尝试使用标签: 我尚未在文档中找到如何执行此操作的示例或其他内容。另外,对于如何实现我想做的更好的方法,您有任何建议,请告诉我:)。 问题答案: 那是因为没有,只有。 好消息是,Spring会根据需要在列表和数组之间进行自动转换,因此将数组定义为,Spring会为您强制将其转换为数组。 这

  • 我将Spring Boot与Spring集成在一起,我希望为每个<code>child() 此时此刻,我正在处理这个:(只有最相关的行) 我已经查看了SpringApplication ationBuilder方法,并且属性从父亲传播到孩子: 但是我需要动态加载一些属性,如下例所示: 从此示例中提取:Spring多个 imapAdapter 这是因为一些Spring集成组件将从配置文件中动态加载。

  • 我有两个类(实际上是一个基类和许多其他类)。我希望获得子类中的父上下文,而不必每次都填充回< code>super()。它的基本目标是把我的角分量分成多个类。我会试着做一个例子 如您所见,我无法检索<code>这个。canvas并使用它,是否有任何解决方法。我知道我可以将画布传递到方法中,但我更希望像组件中一样使用<code>this<code>关键字来访问全局上下文。 所以基本上我想做的是: 任

  • 我有一个关于Spring的ApplicationListener在父上下文和子上下文方面的性质的问题。假设您创建了一个父上下文,它创建了一个bean,该bean是一个单例,并注册为ApplicationListener。然后,使用父上下文创建子上下文。关闭子上下文时,Spring将发送ContextClosedEvent。该事件是否也会传播到父上下文,从而导致作为ApplicationListen