Issue
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:
- Create a "common-jar", and put the above interface under package
com.company.api
. - 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.
Solution
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)
})
Answered By - Ken Chan