Close

Using a Servlet Filter as a Spring Bean with DelegatingFilterProxy

[Last Updated: Sep 28, 2026]

DelegatingFilterProxy is a standard Servlet Filter that acts as a bridge between the Servlet container and the Spring application context.

It does no filtering itself. Instead, it looks up a Spring-managed bean that implements the Filter interface (jakarta.servlet.Filter in current Spring versions) and delegates each request to it.

This matters because a filter registered directly with the container is created by the container, not by Spring, so it cannot use dependency injection. With DelegatingFilterProxy, the real filter is an ordinary Spring bean, so it can be injected with other beans (such as services) and take part in the Spring bean lifecycle.

The bean to delegate to is identified by the proxy's targetBeanName property. If this property is not set, the proxy uses its own filter name as the bean name.

Let's see an example of how to use it.

Example

A Spring bean implementing Servlet Filter

package com.logicbig.example;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.FilterConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import java.io.IOException;

@Component("myTestFilter")
public class MyFilter implements Filter {

    @Autowired
    private MyService myService;

    @Override
    public void init (FilterConfig filterConfig) throws ServletException {
    }

    @Override
    public void doFilter (ServletRequest request, ServletResponse response,
                          FilterChain chain)
              throws IOException, ServletException {
        System.out.println("-- In MyFilter --");
        HttpServletRequest req = (HttpServletRequest) request;
        myService.doSomething(req);
        chain.doFilter(request, response);
    }

    @Override
    public void destroy () {

    }
}
package com.logicbig.example;

import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

import jakarta.servlet.http.HttpServletRequest;

@Service
public class MyService {

    public void doSomething (HttpServletRequest req) {
        System.out.println("In MyService: " + req.getRequestURI());
    }
}

Java configuration class

package com.logicbig.example;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;

@EnableWebMvc
@Configuration
@ComponentScan
public class MyWebConfig {
}

Registering DelegatingFilterProxy

package com.logicbig.example;

import org.springframework.web.filter.DelegatingFilterProxy;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

import jakarta.servlet.Filter;

public class AppInitializer extends
          AbstractAnnotationConfigDispatcherServletInitializer {
    .............
    @Override
    protected Filter[] getServletFilters () {
        DelegatingFilterProxy filterProxy = new DelegatingFilterProxy();
        filterProxy.setTargetBeanName("myTestFilter");
        return new Filter[]{filterProxy};
    }
    .............
}

The targetBeanName must match the name of the Spring bean, which is myTestFilter in this example. Filters returned from getServletFilters() are registered by Spring and mapped to the DispatcherServlet.

A Controller

package com.logicbig.example;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
@RequestMapping("/test")
public class MyController {

    @RequestMapping
    @ResponseBody
    public String handleRequest () {
        System.out.println("-- handling request in controller --");
        return "dummy response";
    }
}

To try examples, run embedded Jetty (configured in pom.xml of example project below):

mvn jetty:run

Output

$ curl -s "http://localhost:8080/test"
dummy response

Server Output


-- In MyFilter --
In MyService: /test
-- handling request in controller --

Things to know

  • Context lookup: the proxy looks for the target bean in the root WebApplicationContext first. If the application has no root context, it falls back to the single DispatcherServlet context, which is why this example works with getRootConfigClasses() returning null. In applications with a root context or several servlets, define the filter bean in the root configuration so the lookup is unambiguous.
    For example:
    com.logicbig.example.core: MyFilter, MyService, MyRootConfig
    com.logicbig.example.web: MyController, MyWebConfig
    @EnableWebMvc
    @Configuration
    @ComponentScan("com.logicbig.example.web")
    public class MyWebConfig {
    }
    
    @Configuration
    @ComponentScan("com.logicbig.example.core")
    public class MyRootConfig {
    }
    
    public class AppInitializer extends
        AbstractAnnotationConfigDispatcherServletInitializer {
    
        @Override
        protected Class<?>[] getRootConfigClasses () {
            return new Class<?>[]{MyRootConfig.class};
        }
    
        @Override
        protected Class<?>[] getServletConfigClasses () {
            return new Class<?>[]{MyWebConfig.class};
        }
    
        @Override
        protected Filter[] getServletFilters () {
            return new Filter[]{
                new DelegatingFilterProxy("myTestFilter")};
        }
    
        @Override
        protected String[] getServletMappings () {
            return new String[]{"/"};
        }
    }
    
  • Lifecycle: by default, Spring manages the target bean's lifecycle, so the container does not call the delegate's init() and destroy(). To have them called, set targetFilterLifecycle to true.
  • Shorter form: the constructor new DelegatingFilterProxy("myTestFilter") sets the target bean name in one step.
  • Spring Boot: you normally don't need this. Boot registers any Filter bean automatically; use FilterRegistrationBean to control the URL patterns and order. For a lazily resolved delegate, Boot provides DelegatingFilterProxyRegistrationBean.
  • Spring Security: the same mechanism is used to plug in the springSecurityFilterChain bean.

Example Project

Dependencies and Technologies Used:

  • spring-webmvc 7.0.6 (Spring Web MVC)
     Version Compatibility: 4.2.0.RELEASE - 7.0.6Version List
    ×

    Version compatibilities of spring-webmvc with this example:

      javax.servlet-api:3.x
    • 4.2.0.RELEASE
    • 4.2.1.RELEASE
    • 4.2.2.RELEASE
    • 4.2.3.RELEASE
    • 4.2.4.RELEASE
    • 4.2.5.RELEASE
    • 4.2.6.RELEASE
    • 4.2.7.RELEASE
    • 4.2.8.RELEASE
    • 4.2.9.RELEASE
    • 4.3.0.RELEASE
    • 4.3.1.RELEASE
    • 4.3.2.RELEASE
    • 4.3.3.RELEASE
    • 4.3.4.RELEASE
    • 4.3.5.RELEASE
    • 4.3.6.RELEASE
    • 4.3.7.RELEASE
    • 4.3.8.RELEASE
    • 4.3.9.RELEASE
    • 4.3.10.RELEASE
    • 4.3.11.RELEASE
    • 4.3.12.RELEASE
    • 4.3.13.RELEASE
    • 4.3.14.RELEASE
    • 4.3.15.RELEASE
    • 4.3.16.RELEASE
    • 4.3.17.RELEASE
    • 4.3.18.RELEASE
    • 4.3.19.RELEASE
    • 4.3.20.RELEASE
    • 4.3.21.RELEASE
    • 4.3.22.RELEASE
    • 4.3.23.RELEASE
    • 4.3.24.RELEASE
    • 4.3.25.RELEASE
    • 4.3.26.RELEASE
    • 4.3.27.RELEASE
    • 4.3.28.RELEASE
    • 4.3.29.RELEASE
    • 4.3.30.RELEASE
    • 5.0.0.RELEASE
    • 5.0.1.RELEASE
    • 5.0.2.RELEASE
    • 5.0.3.RELEASE
    • 5.0.4.RELEASE
    • 5.0.5.RELEASE
    • 5.0.6.RELEASE
    • 5.0.7.RELEASE
    • 5.0.8.RELEASE
    • 5.0.9.RELEASE
    • 5.0.10.RELEASE
    • 5.0.11.RELEASE
    • 5.0.12.RELEASE
    • 5.0.13.RELEASE
    • 5.0.14.RELEASE
    • 5.0.15.RELEASE
    • 5.0.16.RELEASE
    • 5.0.17.RELEASE
    • 5.0.18.RELEASE
    • 5.0.19.RELEASE
    • 5.0.20.RELEASE
    • 5.1.0.RELEASE
    • 5.1.1.RELEASE
    • 5.1.2.RELEASE
    • 5.1.3.RELEASE
    • 5.1.4.RELEASE
    • 5.1.5.RELEASE
    • 5.1.6.RELEASE
    • 5.1.7.RELEASE
    • 5.1.8.RELEASE
    • 5.1.9.RELEASE
    • 5.1.10.RELEASE
    • 5.1.11.RELEASE
    • 5.1.12.RELEASE
    • 5.1.13.RELEASE
    • 5.1.14.RELEASE
    • 5.1.15.RELEASE
    • 5.1.16.RELEASE
    • 5.1.17.RELEASE
    • 5.1.18.RELEASE
    • 5.1.19.RELEASE
    • 5.1.20.RELEASE
    • 5.2.0.RELEASE
    • 5.2.1.RELEASE
    • 5.2.2.RELEASE
    • 5.2.3.RELEASE
    • 5.2.4.RELEASE
    • 5.2.5.RELEASE
    • 5.2.6.RELEASE
    • 5.2.7.RELEASE
    • 5.2.8.RELEASE
    • 5.2.9.RELEASE
    • 5.2.10.RELEASE
    • 5.2.11.RELEASE
    • 5.2.12.RELEASE
    • 5.2.13.RELEASE
    • 5.2.14.RELEASE
    • 5.2.15.RELEASE
    • 5.2.16.RELEASE
    • 5.2.17.RELEASE
    • 5.2.18.RELEASE
    • 5.2.19.RELEASE
    • 5.2.20.RELEASE
    • 5.2.21.RELEASE
    • 5.2.22.RELEASE
    • 5.2.23.RELEASE
    • 5.2.24.RELEASE
    • 5.2.25.RELEASE
    • 5.3.0
    • 5.3.1
    • 5.3.2
    • 5.3.3
    • 5.3.4
    • javax.servlet-api:4.x
    • 5.3.5
    • 5.3.6
    • 5.3.7
    • 5.3.8
    • 5.3.9
    • 5.3.10
    • 5.3.11
    • 5.3.12
    • 5.3.13
    • 5.3.14
    • 5.3.15
    • 5.3.16
    • 5.3.17
    • 5.3.18
    • 5.3.19
    • 5.3.20
    • 5.3.21
    • 5.3.22
    • 5.3.23
    • 5.3.24
    • 5.3.25
    • 5.3.26
    • 5.3.27
    • 5.3.28
    • 5.3.29
    • 5.3.30
    • 5.3.31
    • 5.3.32
    • 5.3.33
    • 5.3.34
    • 5.3.35
    • 5.3.36
    • 5.3.37
    • 5.3.38
    • 5.3.39
    • javax.* -> jakarta.*
      jakarta.servlet-api:6.x
      Java 17 min
    • 6.0.0
    • 6.0.1
    • 6.0.2
    • 6.0.3
    • 6.0.4
    • 6.0.5
    • 6.0.6
    • 6.0.7
    • 6.0.8
    • 6.0.9
    • 6.0.10
    • 6.0.11
    • 6.0.12
    • 6.0.13
    • 6.0.14
    • 6.0.15
    • 6.0.16
    • 6.0.17
    • 6.0.18
    • 6.0.19
    • 6.0.20
    • 6.0.21
    • 6.0.22
    • 6.0.23
    • 6.1.0
    • 6.1.1
    • 6.1.2
    • 6.1.3
    • 6.1.4
    • 6.1.5
    • 6.1.6
    • 6.1.7
    • 6.1.8
    • 6.1.9
    • 6.1.10
    • 6.1.11
    • 6.1.12
    • 6.1.13
    • 6.1.14
    • 6.1.15
    • 6.1.16
    • 6.1.17
    • 6.1.18
    • 6.1.19
    • 6.1.20
    • 6.1.21
    • 6.2.0
    • 6.2.1
    • 6.2.2
    • 6.2.3
    • 6.2.4
    • 6.2.5
    • 6.2.6
    • 6.2.7
    • 6.2.8
    • 6.2.9
    • 6.2.10
    • 6.2.11
    • 6.2.12
    • 6.2.13
    • 6.2.14
    • 6.2.15
    • 6.2.16
    • 6.2.17
    • 6.2.18
    • 6.2.19
    • 7.0.0
    • 7.0.1
    • 7.0.2
    • 7.0.3
    • 7.0.4
    • 7.0.5
    • 7.0.6

    Versions in green have been tested.

  • jakarta.servlet-api 6.1.0 (Jakarta Servlet API documentation)
  • JDK 25
  • Maven 3.9.11

Using Servlet Filter as Spring Bean Select All Download
  • servlet-filter-as-spring-bean
    • src
      • main
        • java
          • com
            • logicbig
              • example
                • MyFilter.java

    See Also

    Join