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

Как я могу получить 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?

4b9b3361

Ответ 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-компонентам между контекстами.