Подтвердить что ты не робот

Объявление Spring Bean в контексте родительского контекста и дочернего контекста

У меня есть объект spring bean (dao), который я создаю в своем ServletContext с помощью следующего xml:

<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 является автообновленным в beans, объявленным в моем другом контексте, но не внутри моего контекста безопасности. В соответствии с документами 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>

Итак, мой вопрос: как я могу получить доступ к моему DAO, который живет в моем ServletContext внутри моего SecurityContext? Есть ли модификатор области моего dao или я могу каким-то образом получить ServletContext во время выполнения в моем провайдере проверки подлинности? Для справки, вот как я хочу использовать его внутри моего провайдера проверки подлинности:

public class UserAuthenticationProvider extends
    AbstractUserDetailsAuthenticationProvider {

    @Override
protected UserDetails retrieveUser(String userName,
        UsernamePasswordAuthenticationToken authenticationToken)
        throws AuthenticationException {

    // use dao here

спасибо, что объяснили это мне

UPDATE:

Продолжая мое исследование, кажется, что DispatcherServlet, где я использую мои daos, представляет собой дочерний контекст, а контекст безопасности - где-то выше. Следовательно, beans в моем DispatcherServlet не может быть замечен родительскими контекстами. Я думаю, что ответ заключается в том, чтобы каким-то образом переносить объявления 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" />

Даос-прежнему остается видимым в контексте 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-UserDetailsService-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 beans создается таким образом, я удалил предыдущий оператор import import = "spring -dao.xml" в файле security.xml, потому что он больше не понадобится.

Сразу после этой конфигурации context-param я создал ContextLoaderListener. Это контейнер более высокого уровня spring, чем DispatcherServlet, поэтому я решил, что первым первым будет читать эти файлы конфигурации, и тогда он создаст beans. Затем любой дочерний контекст будет иметь к ним доступ. Возможно, это не так, как это работает, поскольку DispatcherServlet может даже не читать contextConfigLocation, но даже если это так, я понял, что на данный момент beans уже будет объявлен, что слишком плохо, родительский контекст им владеет.

Теперь, для другого трюка... чтобы получить мой DAO, я не мог @Autowired его. Мне пришлось вручную вводить его через XML:

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

Конечно, я использовал методы getter и setter на моем dao и voila! Я не знаю, почему @Autowired не работает здесь. Я предполагаю это по дизайну. Возможно, это особенно относится к SecurityContext (он не будет тянуть из других контекстов), или, возможно, @Autowired вообще вытягивает только из текущего контекста, или, может быть, потому, что я создал bean через XML, я также должен установить любые свойства через xml, а не через аннотации? (аннотации включены и работают в моем пространстве имен приложений верхнего уровня).

В любом случае.. все еще много не понимаю, но важно то, что он наконец работает.

4b9b3361

Ответ 1

Если вы собираетесь использовать Spring MVC, вам определенно нужно понять Spring MVC ApplicationContext иерархию. Вы также должны узнать что-то о основных компонентах и ​​жизненных циклах в контейнере сервлетов, так как вы, похоже, смущены тем, как работают слушатели и сервлеты.

Чтобы кратко объяснить вашу ситуацию:

  • Вы создаете два ApplicationContexts: корневой контекст и контекст DispatcherServlet. Корневой контекст создается ContextLoaderListener на основе файлов, названных в contextConfigLocation. Этот контекст предназначен для размещения beans, которые составляют основную логику вашего приложения. Контекст DispatcherServlet создается при запуске этого сервлета и основывается на файле с именем "webapp-servlet.xml". Этот контекст предназначен для размещения любого beans, который поддерживает экземпляр DispatcherServlet, с которым он связан, и должен иметь только вид beans.
  • Контекст DispatcherServlet становится потомком корневого контекста. Это позволяет вашему основному beans из корневого контекста вводиться в слой уровня beans. Видимость односторонняя. Уровень представления beans недоступен для ядра beans, что желательно. Вот почему ваш DAO не может быть введен в ваш провайдер проверки подлинности. DAO находилась в детском контексте.
  • Услуги, основанные на аннотациях, применяются только в том контексте, в котором они объявлены. Если @Autowired не работает для определенного bean, это потому, что вы не объявили <context:component-scan/> или <context:annotation-config/> в контексте, где существует bean.