Объявление Spring Bean в родительском контексте против дочернего контекста - PullRequest
24 голосов
/ 13 октября 2011

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

<bean id="userDao" class="com.company.dao.impl.UserDaoImpl">
    <property name="sessionFactory" ref="sessionFactory" />
</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>

, я получаю ошибки, говорящие, что такого бина нет "userDao».Бин автоматически подключается к бинам, объявленным в моем другом контексте, но не в моем контексте безопасности.Согласно 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>

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

public class UserAuthenticationProvider extends
    AbstractUserDetailsAuthenticationProvider {

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

    // use dao here

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

ОБНОВЛЕНИЕ:

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

Итак, проблема заключалась в том, что мы должны убедиться, чтоДао существует в ApplicationContext (контейнер с более высокой пружиной).Чтобы убедиться, что это произошло, я изменил свой файл 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),тогда мой конфиг безопасности.Так как компоненты dao создаются таким образом, я удалил предыдущий оператор import resource = "spring-dao.xml" "в security.xml, потому что он больше не будет нужен.

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

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

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

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

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

1 Ответ

60 голосов
/ 13 октября 2011

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

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

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