如何在 servlet 过滤器中获取 Spring bean?
Posted
技术标签:
【中文标题】如何在 servlet 过滤器中获取 Spring bean?【英文标题】:How can I get a Spring bean in a servlet filter? 【发布时间】: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,感觉不那么特别。 【参考方案1】:有三种方式:
使用WebApplicationContextUtils
:
public void init(FilterConfig cfg)
ApplicationContext ctx = WebApplicationContextUtils
.getRequiredWebApplicationContext(cfg.getServletContext());
this.bean = ctx.getBean(YourBeanType.class);
使用DelegatingFilterProxy
- 您映射该过滤器,并将您的过滤器声明为 bean。然后,委托代理将调用所有实现 Filter
接口的 bean。
在您的过滤器上使用@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
【讨论】:
以上是关于如何在 servlet 过滤器中获取 Spring bean?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 Spring/Servlets 支持批量 Web api 请求处理
我们可以在 Spring Webflux 中使用 web servlet 和 servlet 过滤器吗?
Spring Security:在 servlet 过滤器中访问当前经过身份验证的用户