Как я могу получить Spring bean в фильтре сервлета?
Я определил javax.servlet.Filter
и у меня есть класс Java с аннотациями Spring.
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Bean;
@Configuration
public class SocialConfig {
// ...
@Bean
public UsersConnectionRepository usersConnectionRepository() {
// ...
}
}
Я хочу получить бин UsersConnectionRepository
в моем Filter
, поэтому я попробовал следующее:
public void init(FilterConfig filterConfig) throws ServletException {
UsersConnectionRepository bean = (UsersConnectionRepository) filterConfig.getServletContext().getAttribute("#{connectionFactoryLocator}");
}
Но это всегда возвращает null
. Как я могу получить бин Spring в Filter
?
Ответы
Ответ 1
Try:
UsersConnectionRepository bean =
(UsersConnectionRepository)WebApplicationContextUtils.
getRequiredWebApplicationContext(filterConfig.getServletContext()).
getBean("usersConnectionRepository");
Где usersConnectionRepository
- имя/идентификатор вашего bean в контексте приложения. Или еще лучше:
UsersConnectionRepository bean = WebApplicationContextUtils.
getRequiredWebApplicationContext(filterConfig.getServletContext()).
getBean(UsersConnectionRepository.class);
Также посмотрите GenericFilterBean и его подклассы.
Ответ 2
Существует три способа:
-
Используйте WebApplicationContextUtils
:
public void init(FilterConfig cfg) {
ApplicationContext ctx = WebApplicationContextUtils
.getRequiredWebApplicationContext(cfg.getServletContext());
this.bean = ctx.getBean(YourBeanType.class);
}
-
Используя DelegatingFilterProxy
- вы сопоставляете этот фильтр и объявляете свой фильтр как bean. Затем делегирующий прокси вызовет все beans, которые реализуют интерфейс Filter
.
-
Используйте @Configurable
в своем фильтре. Однако я предпочел бы один из двух других вариантов. (Эта опция использует аспектное плетение)
Ответ 3
Spring имеют утилиту только для этого.
В вашем коде фильтра переопределите метод init следующим образом:
public void init(FilterConfig cfg) {
super.init(cfg);
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
}
Затем вы просто вставляете beans в этот фильтр, как и любой другой bean, который вы вставляете.
@Inject
private UsersConnectionRepository repository;
Ответ 4
расширяет этот ниже класс.
abstract public class SpringServletFilter implements Filter{
@Override
public void init(FilterConfig filterConfig) throws ServletException {
//must provide autowiring support to inject SpringBean
SpringBeanAutowiringSupport.processInjectionBasedOnServletContext(this, filterConfig.getServletContext());
}
@Override
public void destroy() { }
abstract public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException;
}
Ответ 5
Чтобы иметь четкое представление о том, как можно получить доступ к bean-компонентам между контекстами
Spring есть два типа контекста
1. Корневой контекст (контекст, загруженный ContextLoaderListener)
2. Контекст сервлета (контекст, загруженный DispatcherServlet)
Бины, определенные в rootContext, видны в servletContext? - ДА
Бины, определенные в корневом контексте, по умолчанию всегда видимы во всех контекстах сервлета. например, к компоненту dataSource, определенному в корневом контексте, можно обращаться в контексте сервлета, как указано ниже.
@Configuration
public class RootConfiguration
{
@Bean
public DataSource dataSource()
{
...
}
}
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "com.pvn.mvctiles")
public class ServletConfiguration implements WebMvcConfigurer
{
@Autowired
private DataSource dataSource;
...
}
Видны ли bean-компоненты, определенные в servletContext, в rootContext - да *
(Почему * в Да)
1. Инициализация порядка контекста - сначала rootContext, а затем servletContext.
Во время инициализации rootContext, т.е. в классе конфигурации корневого контекста /xml, если вы попытаетесь получить bean-компонент, определенный в servletContext, вы получите NULL. (поскольку servletContext еще не инициализирован, поэтому мы можем сказать, что bean-компоненты не видны/не зарегистрированы во время инициализации rootContext)
Но вы можете получить bean-компоненты, определенные в servletContext, после инициализации servletContext (вы можете получить bean-компоненты через контекст приложения)
Вы можете распечатать и подтвердить это
applicationContext.getBeanDefinitionNames();
2. Если вы хотите получить доступ к bean-компонентам контекста сервлета в фильтре или в другом контексте сервлета, добавьте базовый пакет "org.springframework.web.servlet"
в корневой конфигурационный класс /xml
@Configuration
@ComponentScan(basePackages = "org.springframework.web.servlet" )
public class RootConfiguration
после добавления вы можете получить все приведенные ниже компоненты из контекста приложения
springSecurityConfig
, tilesConfigurer
, themeSource
, themeResolver
, messageSource
, localeResolver
, requestMappingHandlerMapping
, mvcPathMatcher
, mvcUrlPathHelper
, mvcContentNegotiationManager
, viewControllerHandlerMapping
, beanNameHandlerMapping
, resourceHandlerMapping
, mvcResourceUrlProvider
, defaultServletHandlerMapping
, requestMappingHandlerAdapter
, mvcConversionService
, mvcValidator
, mvcUriComponentsContributor
, httpRequestHandlerAdapter
, simpleControllerHandlerAdapter
, handlerExceptionResolver
, mvcViewResolver
, mvcHandlerMappingIntrospector
Если вы хотите получить свои пользовательские bean-компоненты в rootContext, добавьте значение базового пакета к проверке компонента rootContext, как указано ниже.
@Configuration
@ComponentScan(basePackages = { "com.your.configuration.package", "org.springframework.web.servlet" })
public class RootConfiguration
Вышеуказанная конфигурация будет полезна, если вы хотите, чтобы внедренная зависимость была доступна в вашем rootContext и могла быть доступна в вашем сервлет-фильтре. например, если вы перехватываете исключение в фильтре и хотите отправить ответ об ошибке, который совпадает с ответом, отправленным HttpMessageConverter
, но настроен в servletContext, то вы можете обратиться к этому сконфигурированному конвертеру, чтобы отправить тот же ответ.
Обратите внимание, что ниже автопроводка не будет работать в сервлет-фильтрах
@Autowired
private ApplicationContext appContext;
Автоматическая разводка ApplicationContext не будет работать в фильтре сервлета, так как фильтры инициализируются до инициализации контейнера Spring. (Зависит от порядка вашего фильтра и DelegatingProxyFilter)
Итак, чтобы получить applicationContext в фильтре
public class YourFilter implements Filter
{
private ApplicationContext appContext;
@Override
public void init(FilterConfig filterConfig) throws ServletException
{
Filter.super.init(filterConfig);
appContext = WebApplicationContextUtils.getRequiredWebApplicationContext(filterConfig.getServletContext());
}
}
Надеюсь, это дает четкое представление о том, как можно получить доступ к bean-компонентам между контекстами.