【问题标题】:How can I get a Spring bean in a servlet filter?如何在 servlet 过滤器中获取 Spring bean?
【发布时间】:2011-12-14 11:40:22
【问题描述】:

我已经定义了一个javax.servlet.Filter,并且我有一个带有 Spring 注释的 Java 类。

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Bean;

@Configuration
public class SocialConfig {

    // ...

    @Bean
    public UsersConnectionRepository usersConnectionRepository() {
        // ...
    }
}

我想在我的Filter 中获取 bean UsersConnectionRepository,所以我尝试了以下操作:

public void init(FilterConfig filterConfig) throws ServletException {
    UsersConnectionRepository bean = (UsersConnectionRepository) filterConfig.getServletContext().getAttribute("#{connectionFactoryLocator}");
}

但它总是返回null。如何在Filter 中获取 Spring bean?

【问题讨论】:

  • 我更喜欢 this post 中讨论的 DelegatingFilterProxy,感觉不那么特别。

标签: java spring servlets servlet-filters


【解决方案1】:

有三种方式:

  1. 使用WebApplicationContextUtils:

    public void init(FilterConfig cfg) { 
        ApplicationContext ctx = WebApplicationContextUtils
          .getRequiredWebApplicationContext(cfg.getServletContext());
        this.bean = ctx.getBean(YourBeanType.class);
    }
    
  2. 使用DelegatingFilterProxy - 您映射该过滤器,并将您的过滤器声明为 bean。然后,委托代理将调用所有实现 Filter 接口的 bean。

  3. 在您的过滤器上使用@Configurable。不过,我更喜欢其他两个选项之一。 (此选项使用 aspectj 编织)

【讨论】:

  • 恕我直言,第二个是要走的路。比第一选择更具可测试性。
  • 这个回复中缺少的SpringBeanAutowiringSupport方式,我相信是一种更好的方式——更简洁。
【解决方案2】:

试试:

UsersConnectionRepository bean = 
  (UsersConnectionRepository)WebApplicationContextUtils.
    getRequiredWebApplicationContext(filterConfig.getServletContext()).
    getBean("usersConnectionRepository");

usersConnectionRepository 是应用程序上下文中 bean 的名称/ID。甚至更好:

UsersConnectionRepository bean = WebApplicationContextUtils.
  getRequiredWebApplicationContext(filterConfig.getServletContext()).
  getBean(UsersConnectionRepository.class);

还可以查看GenericFilterBean 及其子类。

【讨论】:

    【解决方案3】:

    Spring 有一个专门用于此的实用程序。

    在您的过滤器代码中,像这样覆盖 init 方法:

    public void init(FilterConfig cfg) { 
        super.init(cfg);
        SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
    }
    

    然后你只需 @Inject 你的 bean 到那个过滤器,就像你要注入的任何其他 bean 一样。

    @Inject
    private UsersConnectionRepository repository;
    

    【讨论】:

      【解决方案4】:

      清楚地了解如何在上下文之间访问 bean

      spring 中有两种上下文
      1. 根上下文(Context's Loaded by ContextLoaderListener)
      2.Servlet上下文(上下文由DispatcherServlet加载)

      在 rootContext 中定义的 bean 在 servletContext 中是否可见? - 是的

      默认情况下,在根上下文中定义的 Bean 在所有 servlet 上下文中始终可见。例如,在根上下文中定义的 dataSource bean 可以在 servlet 上下文中访问,如下所示。

      @Configuration
      public class RootConfiguration
      {
          @Bean
          public DataSource dataSource()
          {
             ...
          }
      }
      
      @Configuration
      @EnableWebMvc
      @ComponentScan(basePackages = "com.pvn.mvctiles")
      public class ServletConfiguration implements WebMvcConfigurer
      {
          @Autowired
          private DataSource dataSource;
      
          ...
      }
      

      在 servletContext 中定义的 bean 在 rootContext 中是否可见 - 是*

      (为什么 * 在是)
      1.上下文顺序的初始化是先rootContext,后是servletContext。 在 rootContext 初始化期间,即在根上下文配置类/xml 中,如果您尝试获取在 servletContext 中定义的 bean,您将获得 NULL。 (因为 servletContext 尚未初始化,因此我们可以说 bean 在 rootContext 初始化期间不可见/未注册)
      但是可以在 servletContext 初始化后获取 servletContext 中定义的 bean(可以通过应用上下文获取 bean)

      您可以通过

      打印并确认
      applicationContext.getBeanDefinitionNames();
      


      2. 如果要在过滤器或另一个 servlet 上下文中访问 servlet 上下文的 bean,请将 "org.springframework.web.servlet" 基本包添加到您的根配置类/xml

      @Configuration
      @ComponentScan(basePackages = "org.springframework.web.servlet" )
      public class RootConfiguration
      

      添加后,您可以从应用程序上下文中获取以下所有 bean

      springSecurityConfig, tilesConfigurer, themeSource, themeResolver, messageSource, localeResolver, requestMappingHandlerMapping, mvcPathMatcher, mvcUrlPathHelper, beanNameHandlerMapping, @98654336@, @987654336 ,resourceHandlerMapping,mvcResourceUrlProvider,defaultServletHandlerMapping,requestMappingHandlerAdapter,mvcConversionService,mvcValidator,mvcUriComponentsContributor,httpRequestHandlerAdapter,httpRequestHandlerAdapter,simpleControllerHandlerAdapter,mvcViewResolver,mvcViewResolver,mvcViewResolver,mvcViewResolver p>

      如果您想在 rootContext 中获取自定义 bean,请将基本包值添加到 rootContext 组件扫描,如下所示。

      @Configuration
      @ComponentScan(basePackages = { "com.your.configuration.package", "org.springframework.web.servlet" })
      public class RootConfiguration
      

      如果您希望注入的依赖项在您的 rootContext 中可用并且可以在您的 servlet-filter 中访问,上述配置将很有帮助。例如,如果您在过滤器中捕获异常并希望发送与HttpMessageConverter 发送的响应相同但在 servletContext 中配置的错误响应,那么您可能希望访问该配置的转换器以发送相同的响应。

      注意这一点,下面的自动装配在 servlet-filters 中不起作用

      @Autowired
      private ApplicationContext appContext;
      

      ApplicationContext 自动装配在 servlet 过滤器中不起作用,因为过滤器是在 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。

      【讨论】:

        【解决方案5】:

        将其扩展至以下类。

        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;
        }
        

        【讨论】:

        • 由于错误,这对我不起作用...Cannot override the final method from GenericFilterBean。我的过滤器父类是OncePerRequestFilter
        • @JohnHenckel 在这种情况下覆盖initFilterBean 方法。
        • filterConfig.getServletContext() 给出nullpointerexception。可能是因为在实例化过滤器时尚未加载弹簧上下文。
        【解决方案6】:

        如果您正在使用 spring security 并扩展 OncePerRequestFilter,还有一个解决方案

        @Component
        public class CustomAuthorizationFilter extends OncePerRequestFilter{
        
            @Autowired
            ResourceConfig resourceConfig;
        
            public CustomAuthorizationFilter() {
                    SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
            }
        ...your code
        ... //resourceConfig will not be null
        
        

        【讨论】:

          猜你喜欢
          • 2013-11-11
          • 1970-01-01
          • 2011-01-25
          • 2015-04-02
          • 2015-05-16
          • 2015-11-14
          • 2016-12-20
          • 2013-04-09
          • 2019-12-23
          相关资源
          最近更新 更多