Spring Interceptor and Bean Scopes - Tuong-Nguyen/Spring GitHub Wiki

Interceptors are for intercepting the controller methods. These are called before the control is passed to the controller method. We can intercept HTTP request and perform some actions on it and then hand over it to controller methods.

Creating a Interceptor

In order to use Interceptor, we can implement HandlerInterceptor interface or extend HandlerInterceptorAdapter class and override the following three methods:

    public class GlobalInterceptor extends HandlerInterceptorAdapter {

    @Autowired
    private HitCounter counter;

    public GlobalInterceptor(){
        System.out.println("Instantiate Interceptor");
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        counter.setHits(counter.getHits() + 1);
        System.out.println("Hits :" + counter.getHits());
        request.setAttribute("currentDate", new Date());
        return super.preHandle(request, response, handler);
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        super.postHandle(request, response, handler, modelAndView);
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        super.afterCompletion(request, response, handler, ex);
    }

}
  • boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) : used to intercept the request before it goes to handler method. This method returns the boolean value.

    If true is returned, then request is processed through another interceptor or to send it to handler method if there are no further interceptors.

    If false is returned, then there is no further processing of this request. Use response object to send response to the client request.

    Object handler is object to handle the request and will point to a HandlerMethod.

  • void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) : Called after HandlerAdapter actually invoked the handler, but before the DispacherServlet renders the view.

    ModelAndview object is injected to additional attributes that can be used in the view page.

  • void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) : is called after the view is rendered and will be called on any outcome of handler execution, thus allows for proper resource cleanup.

Registering Interceptor

In the WebConfig file, declare a bean to return our interceptor and override addInterceptors method to register it.

    @Bean
    public GlobalInterceptor globalInterceptor(){
        return new GlobalInterceptor();
    }

    @Override
    public void  addInterceptors(InterceptorRegistry registry){
        registry.addInterceptor( globalInterceptor()).addPathPatterns("/courses/**");
    }

Use addPathPatterns method to set URL path of request that will be intercepted. With this WebConfig file, the Interceptor will be active. All requests send to application will be intercepted and preHandle() method will be executed.

If multiple Interceptors are configured the preHandle() method is executed in the order of configuration, whereas postHandle() and afterCompletion() methods are invoked in the reverse order.

Setting Bean Scopes

The scope of a bean defines the life cycle and visibility of that bean in the contexts in which it is used. Spring defines the following five types of scopes:

  • singleton : the container create a instance of that bean and all requests for that bean will return the same object.
  • prototype : a bean will return a different instance every time it is requested from container.
  • request : creating a bean instance for each HTTP request.
  • session : creating a bean instance for each HTTP Session.
  • globalSession (application): creating a bean instance for a global HTTP Session or entire application.

Setting scopes for bean Interceptor

Using annotation @Scope

    @Bean
    @Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
    public GlobalInterceptor globalInterceptor(){
        return new GlobalInterceptor();
    }

If value of scope is not specified, default value is singleton.

Example for setting bean scopes and its dependency bean

GlobalInterceptor class depend on HitCounter class as below

public class GlobalInterceptor extends HandlerInterceptorAdapter {

    @Autowired
    private HitCounter counter;

    public GlobalInterceptor(){
        System.out.println("Instantiate Interceptor");
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        counter.setHits(counter.getHits() + 1);
        System.out.println("Hits :" + counter.getHits());
        return super.preHandle(request, response, handler);
    }
    
}

Setting scopes for GlobalInterceptor and HitCounter as the following

    @Bean
    @Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
    public HitCounter hitCounter(){
        return new HitCounter();
    }

    @Bean
    @Scope(value = "singleton", proxyMode = ScopedProxyMode.TARGET_CLASS)
    public GlobalInterceptor globalInterceptor(){
        return new GlobalInterceptor();
    }

As setting scopes above, when requests are sent to application, only an instance of GlobalInterceptor is created for all requests, but an instance of HitCounter is created for each request.

Note: As case above, If parallel requests are sent to application, multiple HitCounter will be created. But at the moment GlobalInterceptor also point to one HitCounter as well as one request is served. So, in this case we should change scope of GlobalInterceptor to request value.