When a handler mapping creates a HandlerExecutionChain, Spring 4.1.x added adaptedInterceptors first and then added mappedInterceptors as shown here.
However, Spring 5.1.x merged adapted interceptors and mapped interceptors into adaptedInterceptors variable and getHandlerExecutionChain returns whatever in the order in adaptedInterceptors variable as shown here. However, when the handler mapping populates adaptedInterceptors, it adds mapped interceptors into adaptedInterceptors first and then adds regular interceptors into adaptedInterceptors later as shown here.
As a result, mapped interceptors are executed first and then regular interceptors are executed later in Spring 5 while regular interceptors were executed first and then mapped interceptors were executed later in Spring 4. Is this an intended change? I noticed this while I am upgrading Spring version from 4.1.x to 5.1.x in my application. How can I override in Spring 5 so that regular interceptors are executed first and then mapped interceptors executed after?
Maybe AbstractHandlerMapping.initApplicationContext should be as following?
@Override
protected void initApplicationContext() throws BeansException {
extendInterceptors(this.interceptors);
initInterceptors(); // init and adapt this.interceptors first
detectMappedInterceptors(this.adaptedInterceptors); // then adapt mapped interceptors
}
Comment From: mykevinjung
The following is a way that I can think to override the behavior. But it's really hacking and I like to have any better way. Any suggestion? By the way, what was the reason of switching the order between interceptors and mapped interceptors? Once I understand, maybe I can find some other way.
public class CustomRequestMappingHandlerMapping extends RequestMappingHandlerMapping {
private final List<HandlerInterceptor> interceptors = new ArrayList<>();
private final List<MappedInterceptor> mappedInterceptors = new ArrayList<>();
@Override
protected void initApplicationContext() throws BeansException
{
super.initApplicationContext();
final HandlerInterceptor[] adaptedInterceptors = this.getAdaptedInterceptors();
if (adaptedInterceptors != null) {
for (HandlerInterceptor interceptor : adaptedInterceptors) {
if (interceptor instanceof MappedInterceptor) {
mappedInterceptors.add((MappedInterceptor) interceptor);
}
else {
interceptors.add(interceptor);
}
}
}
}
@Override
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
// add regular interceptors first as we did in Spring 4.1.x
interceptors.forEach(i -> chain.addInterceptor(i));
// then add mapped interceptors
final String lookupPath = this.getUrlPathHelper().getLookupPathForRequest(request);
for (MappedInterceptor mappedInterceptor : this.mappedInterceptors) {
if (mappedInterceptor.matches(lookupPath, this.getPathMatcher())) {
chain.addInterceptor(mappedInterceptor.getInterceptor());
}
}
return chain;
}
}
Comment From: rstoyanchev
This goes back to the changes for #17272 to allow more uniform treatment of pattern-mapped and regular interceptors in terms of ordering especially when configured through the MVC config.
Comment From: mykevinjung
Thanks @rstoyanchev for your response. I had a look https://github.com/spring-projects/spring-framework/issues/17272. So the issue was that we wanted to put an order explicitly regardless of mapped interceptors or regular interceptors. However, looks like the solution was switching the order between regular interceptors and mapped interceptors. I am not convinced that the solution has really achieved the goal (put an order explicitly regardless of mapped interceptors or regular interceptors). It might have solved the specific use case in the issue #17272 though.
Comment From: rstoyanchev
Indeed, the solution for #17272 made it possible to maintain the order of interceptors provided through the Java config regardless of whether they are mapped or not. For interceptors provided directly to the HandlerMapping, it depends when they are provided relative to the Java config.
Can you register your interceptors through the Java config? Alternatively, the MVC Java config provides a way to supply the RequestMappingHandlerMapping instance by switching to advanced config and overriding a protected method. In Spring Boot you can use WebMvcRegistrations for the same. Perhaps you can use that in order to pre-initialize it with interceptors.
Comment From: mykevinjung
Thanks @rstoyanchev for the information. In our case, the app is kind of a legacy app and there are around 90 interceptors defined via xml definitions, so too many places to update. I decided just to override getHandlerExecutionChain() as in the code above.
If this works with Java config, then should be ok. Closing.