user1884155
user1884155

Reputation: 3736

Allow Spring to have multiple WebMvcConfigurer implementations in different jars

When using Spring Web, in this case for rest endpoints & using Spring Boot 2, I can configure interceptors for my app using by implementing the WebMvcConfigurer interface:

@Configuration
public class SpringWebConfig implements WebMvcConfigurer
{
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new MyInterceptor).addPathPatterns("/api/endpoint/**");
    }
}

I added this interceptor to most of my apps in an automatic fashion by doing the following:

  1. Create a "common-jar", and put the above interface under package
    com.company.api.
  2. In every app, add the package com.company.api to the api scan.

This common package also contains the Interceptor and utility classes to make this interceptor work, so in effect, adding this common-jar would automatically add he interceptor to all operations in the app, which is a similar concept as to what Spring itself does: adding dependencies changes the default configuration of Spring.

The problem I'm facing now is this approach cannot be extended to a second interceptor in a second jar, because I already used the WebMvcConfigurer implementation. and I cannot have two.

I was thinking about maybe using some kind of composite-configurer pattern where we loop over every configurer, collect all interceptors, and then add them once, but unfortunately Spring doesn't allow this. What are my options?

Currently, the approach I took is duplicating the WebMvcConfigurer interface in every app that requires it. I feel sad when something changes, and I have to change the same snippet of code in every single app.

Upvotes: 4

Views: 7266

Answers (2)

Ken Chan
Ken Chan

Reputation: 90467

If I understand your question correctly , basically you want to define some common Interceptors in multiple JARs such that an application can activate these Interceptors by simply including these JARs into their app ?

I was thinking about maybe using some kind of composite-configurer pattern where we loop over every configurer, collect all interceptors, and then add them once, but unfortunately Spring doesn't allow this. What are my options?

Well, if implementation A returns a registry with only interceptor A, and implementation B returns a registry with only interceptor B, would spring combine both registries into one super registry containing both A and B, or would it just pick one, or would it throw an error that there was no unique bean definition ?

Actually , Spring has already implement this feature. When there are multiple WebMvcConfigurer beans , Spring simply loop them one by one and calls their configuration methods. So the end-result is that InterceptorRegistry will contain all interceptors.

If the client application need to activate certain WebMvcConfigurer only, it can simply exclude those JARs containing the WebMvcConfigurer that they don't want.

To take this idea further which allow the application to control which Interceptors to activate down to interceptor level , you could even do the following in each common JAR :

@Configuration
public class SpringWebConfig implements WebMvcConfigurer {

    //Make sure the HandlerInterceptor implementation in this JAR is a bean (e.g mark it as @Component)
    @Autowired
    private List<HandlerInterceptor> interceptors;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {

        for(HandlerInterceptor interceptor : interceptors){
            registry.addInterceptor(interceptor).addPathPatterns("/api/endpoint/**");
        }
    }
}

In the client application , use includeFilters / excludeFilters in @ComponentScan to customise which to interceptors to include. For example, to disable certain Interceptors, you could do :

@ComponentScan(
    basePackages = {"com.company.api"},
    excludeFilters={
         @ComponentScan.Filter(type=FilterType.ASSIGNABLE_TYPE, value=com.company.common.jar1.Inteceptor1.class) ,
         @ComponentScan.Filter(type=FilterType.ASSIGNABLE_TYPE, value=com.company.common.jar2.Inteceptor1.class)
    })

Upvotes: 11

Mukul Bansal
Mukul Bansal

Reputation: 936

If I understand your problem correctly, you don't want to implement all the methods of WebMvcConfigurer in every app. You just want to add relevant interceptors and be done with it.

My approach would be to create an AbstractWebMvcConfigurerImpl by implementing the WebMvcConfigurer in the Common module. Just leave the addInterceptors() abstract and implement other methods. You can then extend that abstract implementation in your every Spring Boot project and just override the addInterceptors() method as per your need.

Also, you can have as many implementations of WebMvcConfigurer as you want in a Spring project. So, if you need to define some common interceptors in the Common module, you can extend the AbstractWebMvcConfigurerImpl in the common module also.

Please note that all your implementations of AbstractWebMvcConfigurerImpl should be annotated with @Configuration

Upvotes: 2

Related Questions