Объявление Spring Bean в родительском контексте против дочернего контекста
У меня есть объект 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>
Я получаю сообщение об ошибке, в котором говорится, что такого компонента "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" />
Стиль daos остается видимым для контекста DispatcherServlet и невидимым для моего SecurityContext.
ОТВЕТЫ:
Хорошо, я понял это. Вот несколько полезных ссылок:
http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/beans.html
http://static.springsource.org/spring-security/site/faq.html
Таким образом, проблема заключалась в том, что нам нужно убедиться, что дао существует в 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, потому что он больше не будет нужен.
Сразу после этой конфигурации context-param я создал ContextLoaderListener. Это более высокоуровневый контейнер Spring, чем DispatcherServlet, поэтому я решил, что если поставить его первым, это будет первый парень, который прочитает эти файлы конфигурации, а затем он создаст bean-компоненты. Тогда любой дочерний контекст будет иметь к ним доступ. Это может быть не так, как это работает, так как DispatcherServlet может даже не прочитать contextConfigLocation, но даже если это произойдет, я решил, что на этом этапе компоненты уже будут объявлены, так что, к сожалению, родительский контекст ими владеет.
Теперь, для другого трюка... чтобы получить мой DAO, я не смог @AutoWired его. Мне пришлось вручную ввести его через XML:
<bean id="userAuthenticationProvider" class="com.company.app.security.UserAuthenticationProvider">
<property name="userDao" ref="userDao" />
</bean>
Конечно, я сделал методы получения и установки на моем дао, и вуаля! Я не знаю, почему @Autowired не работает здесь. Я предполагаю, что это по замыслу. Возможно, это относится к SecurityContext (он не будет извлекаться из других контекстов), или, возможно, @Autowired в целом извлекает только из текущего контекста, или, может быть, потому что я создал бин через XML, я должен также установить любые свойства через xml и не с помощью аннотаций? (аннотации включены и работают в моем пространстве имен приложений верхнего уровня).
В любом случае.. все еще много я не понимаю, но важный момент - это, наконец, работает.
1 ответ
Если вы собираетесь использовать Spring MVC, вам определенно необходимо понять иерархию ApplicationContext в Spring MVC. Вам также следует кое-что узнать об основных компонентах и жизненных циклах в контейнере сервлетов, поскольку вы, похоже, также не понимаете, как работают слушатели и сервлеты.
Чтобы кратко объяснить вашу ситуацию:
- Вы создаете два ApplicationContexts: корневой контекст и контекст DispatcherServlet. Корневой контекст создается ContextLoaderListener на основе файлов, названных в contextConfigLocation. Этот контекст предназначен для хранения компонентов, составляющих основную логику вашего приложения. Контекст DispatcherServlet создается при запуске этого сервлета и основан на файле с именем "webapp-servlet.xml". Предполагается, что этот контекст содержит любые bean-компоненты, которые поддерживают экземпляр DispatcherServlet, с которым он связан, и должны содержать только bean-компоненты, относящиеся к представлению.
- Контекст DispatcherServlet становится дочерним по отношению к корневому контексту. Это позволяет вводить ваши базовые bean-компоненты из корневого контекста в bean-компоненты уровня представления. Видимость односторонняя. Бины уровня представления недоступны для основных бинов, что желательно. Вот почему ваш DAO не может быть введен в ваш провайдер аутентификации. DAO был в контексте детей.
- Сервисы на основе аннотаций применяются только в том контексте, в котором они объявлены. Если @Autowired не работает для определенного компонента, это потому, что вы не объявили
<context:component-scan/>
или же<context:annotation-config/>
в контексте, где этот бин существует.