31

Is it possible to wire a Spring MVC Interceptor using annotations and if so could someone provide me with an example of how to do so?

By wire via annotation I am referring to doing as little in the XML configuration as possible. For example in this configuration file I found at http://www.vaannila.com/spring/spring-interceptors.html;

<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping" p:interceptors-ref="loggerInterceptor" />
<bean id="loggerInterceptor" class="com.vaannila.interceptor.LoggerInterceptor" />

How little configuration could you get away with there? I imagine an @Autowired would remove the need to explicitly declare the bean in line 2, but would it be possible to get rid of line 1 with an annotation as well?

James McMahon
  • 48,506
  • 64
  • 207
  • 283
  • Can you clarify? Do you mean Spring MVC interceptors or AOP interceptors? What do you mean by "wiring" it? – axtavt Dec 08 '10 at 15:36
  • 1
    You might want to see this http://karthikg.wordpress.com/2009/10/12/athandlerinterceptor-for-spring-mvc/ – ajm Apr 26 '11 at 13:37
  • 1
    I know it's been a few years that you've asked this question, but @Markus Kreusch has posted correct answer for the new version of Spring MVC – Alexander Pogrebnyak Apr 25 '13 at 14:13

5 Answers5

80

Stumbled upon this question while searching exactly this. Finally I found out that it works in Spring 3.1 using @EnableWebMVC in conjunction with WebMvcConfigurerAdapter.

Simple Example:

@Configuration
@EnableWebMvc
@ComponentScan(basePackages="webapp.base.package")
public class WebApplicationConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoggerInterceptor());
    }

}
Markus Kreusch
  • 2,071
  • 3
  • 19
  • 33
  • 11
    I think this should be made an accepted answer. One caveat, if you want to autowire your custom interceptor, declare it as `@Bean` in one of the `@Configuration` classes and add create it through its bean method. – Alexander Pogrebnyak Apr 25 '13 at 14:16
  • @AlexanderPogrebnyak, This may be the correct answer for newer versions of Spring, but I am no longer in a position to test Spring annotations and I would be remiss to mark it correct without having personally tested it. – James McMahon Apr 25 '13 at 14:19
  • 1
    refer this as well : http://stackoverflow.com/questions/21228447/spring-interceptors-excludepathpatterns-function-is-not-working-properly – Nikhil Jan 26 '15 at 08:25
  • instead of doing it programatically, its better to follow the mvc:interceptors xml based configuration, this totally isolates the linking between interceptor component and controllers, with this its also possible to link regex based url patterns or multiple urls with interceptors – Hashir Labs May 22 '21 at 14:46
20

As far as I know, there are no ways to configure Spring MVC interceptors without XML at all.

However, there are some simplifications with mvc namespace in the latest versions of Spring 3.0.x (not Spring 3.0.0!):

<mvc:interceptors>
    <bean class="com.vaannila.interceptor.LoggerInterceptor" />
</mvc:interceptors>

See also:

axtavt
  • 239,438
  • 41
  • 511
  • 482
  • Good answer. Is it possible to create interceptor that will handle exceptions? – fastcodejava Jul 19 '11 at 04:57
  • @fastcodejava: No. For handling exceptions you can use[`HandlerExceptionResolver`s](http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/mvc.html#mvc-exceptionhandlers). – axtavt Jul 19 '11 at 08:39
  • This is no longer the case since [WebMvcConfigurer](https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/servlet/config/annotation/WebMvcConfigurer.html) was introduced in Spring 3.1 - see next answer below... – rustyx Apr 13 '20 at 20:27
8

I implemented a working solution using a custom @Interceptor annotation in the spirit of Spring's @Controller annotation:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Component
public @interface Interceptor {
  String[] pathPatterns() default {};
  String[] excludePathPatterns() default {};
}

This annotation should be applied to HandlerInterceptor types like so:

@Interceptor
public class BuildTimestampInterceptor extends HandlerInterceptorAdapter {
  private final String buildTimestamp;

  public BuildTimestampInterceptor(@Value("${build.timestamp}") String buildTimestamp) {
    this.buildTimestamp = buildTimestamp;
  }

  @Override
  public boolean preHandle(HttpServletRequest req, HttpServletResponse res, Object handler) throws Exception {
    req.setAttribute("buildTimestamp", buildTimestamp);
    return true;
  }
}

Finally, the processor class, InterceptorProcessor, is a Spring bean that extends WebMvcConfigurerAdapter and implements BeanPostProcessor in order to scan for the custom @Interceptor annotations and register beans having that anntation as HandlerInterceptors inside the overridden addInterceptors method:

@Component
public class InterceptorProcessor extends WebMvcConfigurerAdapter implements BeanPostProcessor {
  private final Map<HandlerInterceptor,Interceptor> interceptors = new HashMap<>();

  @Override
  public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    scanForInterceptorAnnotation(bean, beanName);
    return bean;
  }

  @Override
  public Object postProcessAfterInitialization(Object bean, String string) throws BeansException {
    return bean;
  }

  protected void scanForInterceptorAnnotation(Object bean, String beanName) {
    Optional<Interceptor> optionalInterceptor = getInterceptorAnnotation(bean.getClass());
    if (optionalInterceptor.isPresent() && bean instanceof HandlerInterceptor) {
      interceptors.put((HandlerInterceptor) bean, optionalInterceptor.get());
    }
  }

  private Optional<Interceptor> getInterceptorAnnotation(Class cls) {
    Annotation[] annotations = cls.getAnnotationsByType(Interceptor.class);
    if (hasValue(annotations)) {
      return Optional.of((Interceptor) annotations[0]);
    }
    return Optional.empty();
  }

  @Override
  public void addInterceptors(InterceptorRegistry registry) {
    interceptors.forEach((HandlerInterceptor key, Interceptor val) -> {
      InterceptorRegistration registration = registry.addInterceptor(key);
      if (hasValue(val.pathPatterns())) {
        registration.addPathPatterns(val.pathPatterns());
      }

      if (hasValue(val.excludePathPatterns())) {
        registration.excludePathPatterns(val.excludePathPatterns());
      }
    });
  }

  private static <T> boolean hasValue(T[] array) {
    return array != null && array.length > 0;
  }
}

Just remember to have your spring application scan for this processor bean in order to have it actually register your @Interceptors. Something like:

@Configuration
@EnableWebMvc
@ComponentScan(basePackages = {"org.my.controller", "org.my.utils.processor"})
public class WebConfig extends WebMvcConfigurerAdapter {...
Brice Roncace
  • 10,110
  • 9
  • 60
  • 69
0

I dont know about spring-AOP but if you're using AspectJ via Spring you can use @Aspect, @Pointcut, @Advise and more...

there's also a nice article on howto use these annotation with Spring AOP here: http://java-x.blogspot.com/2009/07/spring-aop-with-aspecj-annotations.html

fasseg
  • 17,504
  • 8
  • 62
  • 73
-2

like Markus Kreusch'answers,It also could work like this

@Configuration
@EnableWebMvc
@ComponentScan(basePackages="webapp.base.package")
public class WebApplicationConfig extends WebMvcConfigurerAdapter {

    @Override
    public RequestMappingHandlerMapping requestMappingHandlerMapping() {
        RequestMappingHandlerMapping RequestMappingHandlerMapping=  super.requestMappingHandlerMapping();
        Object[] interceptors = new Object[1];
        interceptors[0] = new RoleInterceptor();
        RequestMappingHandlerMapping.setInterceptors(interceptors);
        return RequestMappingHandlerMapping;
    }

}
吴志坚
  • 29
  • 4
  • 1
    This has not improved Markus' answer in any way. Your answer actually confused more than it sheds light to the problem. – javaauthority Jun 05 '16 at 04:59