1. The following code caused my custom HandlerMethod subclass to be recreated as HandlerMethod,
  2. Every HTTP request calls createWithResolvedBean(), resulting in the creation of a new instance of HandlerMethod. The handler in HandlerMethod should be determined at startup(main thread), rather than being processed after each call

https://github.com/spring-projects/spring-framework/blob/main/spring-web/src/main/java/org/springframework/web/method/HandlerMethod.java#L302-L323

    /**
     * Re-create the HandlerMethod and initialize
     * {@link #shouldValidateArguments()} and {@link #shouldValidateReturnValue()}.
     * @since 6.1.3
     */
    public HandlerMethod createWithValidateFlags() {
        return new HandlerMethod(this, null, true);
    }


    /**
     * If the provided instance contains a bean name rather than an object instance,
     * the bean name is resolved before a {@link HandlerMethod} is created and returned.
     */
    public HandlerMethod createWithResolvedBean() {
        Object handler = this.bean;
        if (this.bean instanceof String beanName) {
            Assert.state(this.beanFactory != null, "Cannot resolve bean name without BeanFactory");
            handler = this.beanFactory.getBean(beanName);
        }
        Assert.notNull(handler, "No handler instance");
        return new HandlerMethod(this, handler, false);
    }

my code

public class ControllerRequestMappingHandlerMapping extends RequestMappingHandlerMapping {

    private final List<ControllerInterceptor> controllerInterceptors;

    public ControllerRequestMappingHandlerMapping(List<ControllerInterceptor> controllerInterceptors) {
        this.controllerInterceptors = controllerInterceptors;
    }

    @Override
    protected HandlerMethod createHandlerMethod(Object handler, Method method) {
        ArrayList<ControllerInterceptor> interceptors = new ArrayList<>();
        Class<?> declaringClass = method.getDeclaringClass();
        for (ControllerInterceptor interceptor : controllerInterceptors) {
            Pointcut pointcut = interceptor.getPointcut();
            if (AopUtils.canApply(pointcut, declaringClass)) {
                interceptors.add(interceptor);
            }
        }
        if (interceptors.size() > 0) {
            if (handler instanceof String beanName) {
                return new InterceptorHandlerMethod(beanName,
                        obtainApplicationContext().getAutowireCapableBeanFactory(),
                        obtainApplicationContext(),
                        method, interceptors);
            }
            return new InterceptorHandlerMethod(handler, method, interceptors);
        }
        return super.createHandlerMethod(handler, method);
    }
}

Comment From: rstoyanchev

The main reason for createWithResolvedBean is to allow the handler, which could be a prototype bean, to be resolved per request through the ApplicationContext. If you're overriding AbstractHandlerMethodMapping#createHandlerMethod, however to fix the handler instance on startup, then there is not reason to re-create the HandlerMethod instance at runtime.

We can update HandlerMethod#createWithResolvedBean along those lines, and you will need to adjust your createHandlerMethod override to call the HandlerMethod constructor with a resolved handler instance.

Comment From: brucelwl

@rstoyanchev 👍

Comment From: brucelwl

@rstoyanchev

Can change private to protected? I need to inherit it and implement my own HandlerMethod

private HandlerMethod(HandlerMethod handlerMethod, @Nullable Object handler, boolean initValidateFlags) 

https://github.com/spring-projects/spring-framework/blob/main/spring-web/src/main/java/org/springframework/web/method/HandlerMethod.java#L178-L198

Comment From: brucelwl

@rstoyanchev Is it possible to change the initialization method of RequestMappingHandlerMapping#afterPropertiesSet and AbstractHandlerMethodMapping#afterPropertiesSet from InitializingBean to SmartInitializingSingleton, otherwise override the createHandlerMethod method to obtain beans may cause BeanCurrentlyInCreationException

Image

Image

Image

Comment From: rstoyanchev

I am re-opening as the changes caused test failures in Boot tests related to CORS configuration.

Comment From: rstoyanchev

@brucelwl, I'll have a look at making the HandlerMethod constructor protected.

In terms of initialization and BeanCurrentlyInCreationException, I'm less inclined to do that, and not in a maintenance release. Something to consider is we have public registerMapping and unregisterMapping methods on AbstractHandlerMethodMapping. So you could get all the mappings via getHandlerMethods after those have been initialized and re-register them as a HandlerMethod subclass with bean instances.

Comment From: brucelwl

@rstoyanchev Thanks, I resolved the BeanCurrentlyInCreationException as you suggested

Comment From: brucelwl

@rstoyanchev I delayed the initialization of the HandlerMethod in the following way to avoid BeanCurrentlyInCreationException, is it feasible?

my custom RequestMappingHandlerMapping

Image

Comment From: rstoyanchev

As I mentioned before, this is not something we will do in a maintenance release. However, I've created #34375, which turns out is also related to a custom HandlerMethod, and will make some improvements for that scenario. I will comment again under that issue when I have more to share,

Comment From: brucelwl

@rstoyanchev

Can change private to protected? I need to inherit it and implement my own HandlerMethod

private HandlerMethod(HandlerMethod handlerMethod, @Nullable Object handler, boolean initValidateFlags) https://github.com/spring-projects/spring-framework/blob/main/spring-web/src/main/java/org/springframework/web/method/HandlerMethod.java#L178-L198

@rstoyanchev Can we solve this problem first and change it to protected, so that I can also custom my own Handler Method, I can override createWithValidateFlags() and createWithResolvedBean() to ensure that all returned HandlerMethodsare my CustomHandlerMethod