How can I get a Spring bean in a servlet filter?
Asked Answered
P

6

48

I have defined a javax.servlet.Filter and I have Java class with Spring annotations.

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

@Configuration
public class SocialConfig {

    // ...

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

I want to get the bean UsersConnectionRepository in my Filter, so I tried the following:

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

But it always returns null. How can I get a Spring bean in a Filter?

Pepito answered 24/10, 2011 at 21:12 Comment(1)
I prefer DelegatingFilterProxy as discussed in this post, feels less ad-hoc.Anciently
C
36

Try:

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

Where usersConnectionRepository is a name/id of your bean in the application context. Or even better:

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

Also have a look at GenericFilterBean and its subclasses.

Christinachristine answered 24/10, 2011 at 21:25 Comment(0)
I
79

There are three ways:

  1. Use WebApplicationContextUtils:

    public void init(FilterConfig cfg) { 
        ApplicationContext ctx = WebApplicationContextUtils
          .getRequiredWebApplicationContext(cfg.getServletContext());
        this.bean = ctx.getBean(YourBeanType.class);
    }
    
  2. Using the DelegatingFilterProxy - you map that filter, and declare your filter as bean. The delegating proxy will then invoke all beans that implement the Filter interface.

  3. Use @Configurable on your filter. I would prefer one of the other two options though. (This option uses aspectj weaving)

Incinerator answered 24/10, 2011 at 21:25 Comment(2)
IMHO, the second one is the way to go. Much more testable than the first choice.Sheerlegs
The SpringBeanAutowiringSupport way, which is missing in this reply, I believe is a better way - cleaner and simple.Natelson
C
36

Try:

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

Where usersConnectionRepository is a name/id of your bean in the application context. Or even better:

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

Also have a look at GenericFilterBean and its subclasses.

Christinachristine answered 24/10, 2011 at 21:25 Comment(0)
N
22

Spring have a utility just for this.

In your Filter code, override the init method like this:

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

Then you just @Inject your beans to that filter, same way as any other bean you would inject.

@Inject
private UsersConnectionRepository repository;
Natelson answered 12/1, 2017 at 8:28 Comment(0)
Q
4

To have clear idea of how beans can be accessed between contexts

There are two types of context in spring
1. Root context (Context's Loaded by ContextLoaderListener)
2. Servlet context (Context's Loaded by DispatcherServlet)

Is beans defined in rootContext are visible in servletContext? - YES

Beans defined in root context is always visible in all servlet contexts by default. for example dataSource bean defined in root context can be accessed in servlet context as given below.

@Configuration
public class RootConfiguration
{
    @Bean
    public DataSource dataSource()
    {
       ...
    }
}

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

    ...
}

Is beans defined in servletContext visible in rootContext - yes*

(Why * in Yes)
1. Initialization of context order is rootContext first and servletContext next. During initialization of rootContext i.e, in the root context configuration class/xml if you try to get the bean defined in servletContext you will get NULL. (because servletContext is not initialized yet, hence we can say beans not visible/registered during initialization of rootContext)
But you can get beans defined in servletContext after initialization of servletContext(you can get beans through application context)

you can print and confirm it by

applicationContext.getBeanDefinitionNames();


2. If you want to access beans of servlet context in the filter or in the another servlet context, add "org.springframework.web.servlet" base package to your root config class/xml

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

after adding you can get all below beans from application context

springSecurityConfig, tilesConfigurer, themeSource, themeResolver, messageSource, localeResolver, requestMappingHandlerMapping, mvcPathMatcher, mvcUrlPathHelper, mvcContentNegotiationManager, viewControllerHandlerMapping, beanNameHandlerMapping, resourceHandlerMapping, mvcResourceUrlProvider, defaultServletHandlerMapping, requestMappingHandlerAdapter, mvcConversionService, mvcValidator, mvcUriComponentsContributor, httpRequestHandlerAdapter, simpleControllerHandlerAdapter, handlerExceptionResolver, mvcViewResolver, mvcHandlerMappingIntrospector

If you want to get your custom beans in rootContext add base package value to rootContext component scan as given below.

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

Above given configuration will be helpful if you want injected dependency be available in your rootContext and can be accessed in your servlet-filter. For example If you catch exception in filter and want to send error response which is same as response sent by HttpMessageConverter but it is configured in servletContext, then you may want to access that configured converter to send the same response.

Note this, below autowiring will not work in servlet-filters

@Autowired
private ApplicationContext appContext;

ApplicationContext autowiring will not work in servlet filter, as filters are initialized before spring container got initialized.(Depends on order of your filter and DelegatingProxyFilter)

So, to get applicationContext in filter

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

Hope it gives clear idea of how beans can be accessed between contexts.

Quickel answered 29/8, 2019 at 14:13 Comment(0)
B
1

extends this below class.

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;
}
Bayern answered 9/10, 2017 at 20:5 Comment(3)
This did NOT work for me because of error... Cannot override the final method from GenericFilterBean. My filter parent class is OncePerRequestFilterLanilaniard
@JohnHenckel In that case override the initFilterBean method.Yalta
filterConfig.getServletContext() gives nullpointerexception. Perhaps because spring context has not loaded at the time filter are instantiated.Damarisdamarra
C
0

One more solution if you are using spring security and extending the OncePerRequestFilter

@Component
public class CustomAuthorizationFilter extends OncePerRequestFilter{

    @Autowired
    ResourceConfig resourceConfig;

    public CustomAuthorizationFilter() {
            SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
    }
...your code
... //resourceConfig will not be null

Cu answered 11/1, 2022 at 17:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.