spring-cloud / spring-cloud-commons

Common classes used in different Spring Cloud implementations
Apache License 2.0
701 stars 697 forks source link

BeanPostProcessorChecker [loadBalancerWebClientBuilderBeanPostProcessor] is declared through a non-static factory method on that class #1340

Closed c-hui closed 4 months ago

c-hui commented 6 months ago

Version:

description:

when startup, have warn messages :

2024-02-21 18:36:11.084 [main] WARN  o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker - Bean 'org.springframework.cloud.client.loadbalancer.reactive.LoadBalancerBeanPostProcessorAutoConfiguration' of type [org.springframework.cloud.client.loadbalancer.reactive.LoadBalancerBeanPostProcessorAutoConfiguration] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). The currently created BeanPostProcessor [loadBalancerWebClientBuilderBeanPostProcessor] is declared through a non-static factory method on that class; consider declaring it as static instead.
2024-02-21 18:36:11.087 [main] WARN  o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker - Bean 'org.springframework.cloud.client.loadbalancer.reactive.LoadBalancerBeanPostProcessorAutoConfiguration$ReactorDeferringLoadBalancerFilterConfig' of type [org.springframework.cloud.client.loadbalancer.reactive.LoadBalancerBeanPostProcessorAutoConfiguration$ReactorDeferringLoadBalancerFilterConfig] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). Is this bean getting eagerly injected into a currently created BeanPostProcessor [loadBalancerWebClientBuilderBeanPostProcessor]? Check the corresponding BeanPostProcessor declaration and its dependencies.
2024-02-21 18:36:11.089 [main] WARN  o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker - Bean 'reactorDeferringLoadBalancerExchangeFilterFunction' of type [org.springframework.cloud.client.loadbalancer.reactive.DeferringLoadBalancerExchangeFilterFunction] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). Is this bean getting eagerly injected into a currently created BeanPostProcessor [loadBalancerWebClientBuilderBeanPostProcessor]? Check the corresponding BeanPostProcessor declaration and its dependencies.

I see it is indeed non-static here: https://github.com/spring-cloud/spring-cloud-commons/blob/v4.1.1/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/reactive/LoadBalancerBeanPostProcessorAutoConfiguration.java#L50

ChunhuiDao commented 5 months ago

I also encountered the same problem, I used the webflux framework and turned on loadbalancer.

springcloud2023.0.0 spring-cloud-commons4.1.1 springboot(spring-boot-starter-webflux)3.2.3

image

trationDelegate$BeanPostProcessorChecker : Bean 'org.springframework.cloud.client.loadbalancer.reactive.LoadBalancerBeanPostProcessorAutoConfiguration' of type [org.springframework.cloud.client.loadbalancer.reactive.LoadBalancerBeanPostProcessorAutoConfiguration] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). The currently created BeanPostProcessor [loadBalancerWebClientBuilderBeanPostProcessor] is declared through a non-static factory method on that class; consider declaring it as static instead.

trationDelegate$BeanPostProcessorChecker : Bean 'org.springframework.cloud.client.loadbalancer.reactive.LoadBalancerBeanPostProcessorAutoConfiguration$ReactorDeferringLoadBalancerFilterConfig' of type [org.springframework.cloud.client.loadbalancer.reactive.LoadBalancerBeanPostProcessorAutoConfiguration$ReactorDeferringLoadBalancerFilterConfig] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). Is this bean getting eagerly injected into a currently created BeanPostProcessor [loadBalancerWebClientBuilderBeanPostProcessor]? Check the corresponding BeanPostProcessor declaration and its dependencies.

trationDelegate$BeanPostProcessorChecker : Bean 'reactorDeferringLoadBalancerExchangeFilterFunction' of type [org.springframework.cloud.client.loadbalancer.reactive.DeferringLoadBalancerExchangeFilterFunction] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). Is this bean getting eagerly injected into a currently created BeanPostProcessor [loadBalancerWebClientBuilderBeanPostProcessor]? Check the corresponding BeanPostProcessor declaration and its dependencies.

ChunhuiDao commented 5 months ago

After tracking down, I found that the initialization order of these two classes was not as expected. In fact, 'LoadBalancerBeanPostProcessorAutoConfiguration' was initialized before 'LoadBalancerAutoConfiguration'.

image

ChunhuiDao commented 5 months ago

I currently have a solution. Exclude this automation configuration 'org.springframework.cloud. Client.loadbalancer.reactive.LoadBalancerBeanPostProcessorAutoConfiguration', customize an automation configuration of 'WebClient. Builder', and add the processing in the 'LoadBalancerWebClientBuilderBeanPostProcessor' to the custom 'WebClient. Builder' configuration.

package org.springframework.cloud.client.loadbalancer.reactive;

import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.web.reactive.function.client.WebClient;

/**
 * An auto-configuration that provides a {@link BeanPostProcessor} that allows the use of
 * a {@link LoadBalanced} {@link WebClient.Builder} with
 * {@link ReactorLoadBalancerExchangeFilterFunction} and {@link ReactiveLoadBalancer} used
 * under the hood. NOTE: This has been extracted to a separate configuration in order to
 * not impact instantiation and post-processing of other Reactor-LoadBalancer-related
 * beans.
 *
 * @author Olga Maciaszek-Sharma
 * @since 2.2.0
 */
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(WebClient.class)
@Conditional(LoadBalancerBeanPostProcessorAutoConfiguration.OnAnyLoadBalancerImplementationPresentCondition.class)
public class LoadBalancerBeanPostProcessorAutoConfiguration {

    @Bean
    public LoadBalancerWebClientBuilderBeanPostProcessor loadBalancerWebClientBuilderBeanPostProcessor(
            DeferringLoadBalancerExchangeFilterFunction deferringExchangeFilterFunction, ApplicationContext context) {
        return new LoadBalancerWebClientBuilderBeanPostProcessor(deferringExchangeFilterFunction, context);
    }

    @Configuration(proxyBeanMethods = false)
    @ConditionalOnBean(ReactiveLoadBalancer.Factory.class)
    protected static class ReactorDeferringLoadBalancerFilterConfig {

        @Bean
        @Primary
        DeferringLoadBalancerExchangeFilterFunction<LoadBalancedExchangeFilterFunction> reactorDeferringLoadBalancerExchangeFilterFunction(
                ObjectProvider<LoadBalancedExchangeFilterFunction> exchangeFilterFunctionProvider) {
            return new DeferringLoadBalancerExchangeFilterFunction<>(exchangeFilterFunctionProvider);
        }

    }

    static final class OnAnyLoadBalancerImplementationPresentCondition extends AnyNestedCondition {

        private OnAnyLoadBalancerImplementationPresentCondition() {
            super(ConfigurationPhase.REGISTER_BEAN);
        }

        @ConditionalOnBean(ReactiveLoadBalancer.Factory.class)
        static class ReactiveLoadBalancerFactoryPresent {

        }

        @ConditionalOnBean(LoadBalancerClient.class)
        static class LoadBalancerClientPresent {

        }

    }

}
package org.springframework.cloud.client.loadbalancer.reactive;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.ApplicationContext;
import org.springframework.web.reactive.function.client.WebClient;

/**
 * A {@link BeanPostProcessor} that applies
 * {@link DeferringLoadBalancerExchangeFilterFunction} filter to all
 * {@link WebClient.Builder} instances annotated with {@link LoadBalanced}.
 *
 * @author Olga Maciaszek-Sharma
 * @since 2.2.0
 */
public class LoadBalancerWebClientBuilderBeanPostProcessor implements BeanPostProcessor {

    private final DeferringLoadBalancerExchangeFilterFunction exchangeFilterFunction;

    private final ApplicationContext context;

    public LoadBalancerWebClientBuilderBeanPostProcessor(
            DeferringLoadBalancerExchangeFilterFunction exchangeFilterFunction, ApplicationContext context) {
        this.exchangeFilterFunction = exchangeFilterFunction;
        this.context = context;
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof WebClient.Builder) {
            if (context.findAnnotationOnBean(beanName, LoadBalanced.class) == null) {
                return bean;
            }
            ((WebClient.Builder) bean).filter(exchangeFilterFunction);
        }
        return bean;
    }

}

The customized configuration of 'WebClient. Builder':

package com.conwayd.common.web.configs;

import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration;
import org.springframework.boot.autoconfigure.web.reactive.function.client.ClientHttpConnectorAutoConfiguration;
import org.springframework.boot.web.reactive.function.client.WebClientCustomizer;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.cloud.client.loadbalancer.reactive.DeferringLoadBalancerExchangeFilterFunction;
import org.springframework.cloud.client.loadbalancer.reactive.LoadBalancedExchangeFilterFunction;
import org.springframework.cloud.client.loadbalancer.reactive.LoadBalancerBeanPostProcessorAutoConfiguration;
import org.springframework.cloud.client.loadbalancer.reactive.ReactiveLoadBalancer;
import org.springframework.cloud.loadbalancer.config.BlockingLoadBalancerClientAutoConfiguration;
import org.springframework.cloud.loadbalancer.config.LoadBalancerAutoConfiguration;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Scope;
import org.springframework.web.reactive.function.client.WebClient;

/**
 * 自定义webclient
 * <br>
 * 修复框架中 {@link LoadBalancerBeanPostProcessorAutoConfiguration#loadBalancerWebClientBuilderBeanPostProcessor(DeferringLoadBalancerExchangeFilterFunction, ApplicationContext)} 的初始化问题
 *
 * @author conwayD at 2024-03-08 10:27
 * @since 1.0.0
 */
@ConditionalOnClass(WebClient.class)
@Conditional(CustomizeWebClientAutoConfiguration.OnAnyLoadBalancerImplementationPresentCondition.class)
@AutoConfiguration(after = {CodecsAutoConfiguration.class, ClientHttpConnectorAutoConfiguration.class})
@AutoConfigureAfter({LoadBalancerAutoConfiguration.class, BlockingLoadBalancerClientAutoConfiguration.class})
public class CustomizeWebClientAutoConfiguration {

    @Bean
    @Primary
    @Scope("prototype")
    public WebClient.Builder webClientBuilder(ObjectProvider<WebClientCustomizer> customizerProvider,
                                              ObjectProvider<LoadBalancedExchangeFilterFunction> exchangeFilterFunctionProvider) {

        WebClient.Builder builder = WebClient.builder();
        customizerProvider.orderedStream().forEach((customizer) -> customizer.customize(builder));

        DeferringLoadBalancerExchangeFilterFunction<LoadBalancedExchangeFilterFunction> filterFunction =
                new DeferringLoadBalancerExchangeFilterFunction<>(exchangeFilterFunctionProvider);
        builder.filter(filterFunction);

        return builder;
    }

    static final class OnAnyLoadBalancerImplementationPresentCondition extends AnyNestedCondition {

        private OnAnyLoadBalancerImplementationPresentCondition() {
            super(ConfigurationPhase.REGISTER_BEAN);
        }

        @ConditionalOnBean(ReactiveLoadBalancer.Factory.class)
        static class ReactiveLoadBalancerFactoryPresent {

        }

        @ConditionalOnBean(LoadBalancerClient.class)
        static class LoadBalancerClientPresent {

        }

    }
}
OlgaMaciaszek commented 4 months ago

Hello @c-hui. Thanks for reporting the issue. This is a duplicate of https://github.com/spring-cloud/spring-cloud-commons/issues/1315. Please upgrade to the newest release.