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

发布于 2024-12-09 19:26:25 字数 6533 浏览 1 评论 0原文

我有一个 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 文档,我相信 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 不可见尽管。

已回答:

好吧,我明白了。这里有一些有用的链接:

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 更改为:

<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 beans),然后读取我的安全配置。由于 dao bean 是以这种方式创建的,因此我删除了 security.xml 中先前的“import resource="spring-dao.xml"”语句,因为不再需要它。

在上下文参数配置之后,我创建了 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 而不是通过注释设置任何属性? (注释已启用并在我的顶级应用程序命名空间中工作)。

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

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>

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

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

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>

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>

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

thanks for explaining this to me

UPDATE:

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>

    ...

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

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

ANSWERED:

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

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

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>

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.

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.

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>

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

_蜘蛛 2024-12-16 19:26:25

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

简要解释一下您的情况:

  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 存在的上下文中。

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.

To explain your situation briefly:

  1. You're creating two ApplicationContexts: the root context and the DispatcherServlet context. The root context is created by the ContextLoaderListener based on the files named in the contextConfigLocation. This context is intended to contain the beans that compose the core logic of your app. The DispatcherServlet context is created when that servlet starts and is based on the file named "webapp-servlet.xml". This context is intended to contain any beans that support the DispatcherServlet instance that it's associated with and should only have view-related beans in it.
  2. The DispatcherServlet context becomes a child of the root context. That allows your core beans from the root context to be injected into the view-layer beans. Visibility is one-way. The view-layer beans aren't available to the core beans, which is desirable. This is why your DAO couldn't be injected into your authentication provider. The DAO was in the child context.
  3. Annotation-based services only apply within the context where they're declared. If @Autowired isn't working for a particular bean, it's because you haven't declared <context:component-scan/> or <context:annotation-config/> in the context where that bean exists.
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文