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

查看:125
本文介绍了在父上下文和子上下文中声明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>

我收到错误消息,说没有这样的bean"userDao".在我的其他上下文中声明的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

感谢您向我解释

更新:

继续我的调查,似乎我在使用dao的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" />

Dao stil对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容器,所以我认为将其放在第一位将是阅读那些配置文件的人,然后 he 将创建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,我可以 @自动装配它.我不得不通过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通常从当前上下文中 only 提取,或者可能是因为我是通过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,则肯定需要 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.当该Servlet启动时将创建DispatcherServlet上下文,并且该上下文基于名为"webapp-servlet.xml"的文件.此上下文旨在包含支持与其关联的DispatcherServlet实例的任何bean,并且其中仅应包含与视图相关的bean.
  2. DispatcherServlet上下文成为根上下文的子级.这样就可以将根上下文中的核心bean注入到视图层bean中.可见性是单向的.视图层bean对核心bean不可用,这是合乎需要的.这就是为什么您的DAO无法注入到身份验证提供程序中的原因. DAO是在子上下文中.
  3. 基于注释的服务仅在声明它们的上下文中适用.如果@Autowired不适用于特定的bean,那是因为您尚未声明 <context:annotation-config/> .

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

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