Preface

In the previous article, we about how the custom implementation of integrates SPI with spring 1619309aab2223, but in fact there is a small detail in the implementation process, that is, our SPI was originally equipped with an interceptor function, (ps: how to Friends who are interested in implementing an SPI with an interceptor can check this article --> talk about how to implement an SPI with an interceptor function).

In order to retain this interceptor function, my original idea was to change the civet cat for the prince and integrate the interceptor function into the post processor provided by spring. The implementation code at that time is as follows

@Slf4j
@Deprecated
public class SpiInstancePostProcessor implements BeanPostProcessor {

    private DefaultListableBeanFactory beanFactory;

    private InterceptorHandler interceptorHandler;

    public SpiInstancePostProcessor(InterceptorHandler interceptorHandler,DefaultListableBeanFactory beanFactory) {
        this.interceptorHandler = interceptorHandler;
        this.beanFactory = beanFactory;
    }


    @SneakyThrows
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if(bean.getClass().isAnnotationPresent(Activate.class)){
            return interceptorHandler.getInterceptorChain().pluginAll(bean);
        }
        return bean;
    }

}

The function is realized, but the following message appears on the console

trationDelegate$BeanPostProcessorChecker : Bean 'interceptorHandler' of type [com.github.lybgeek.spring.interceptor.handler.InterceptorHandler] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)

Investigation process

At that time, the investigation was to find the corresponding source code through the information prompted by the control. exist

org.springframework.context.support.PostProcessorRegistrationDelegate

Find the corresponding implementation

@Override
        public Object postProcessAfterInitialization(Object bean, String beanName) {
            if (!(bean instanceof BeanPostProcessor) && !isInfrastructureBean(beanName) &&
                    this.beanFactory.getBeanPostProcessorCount() < this.beanPostProcessorTargetCount) {
                if (logger.isInfoEnabled()) {
                    logger.info("Bean '" + beanName + "' of type [" + bean.getClass().getName() +
                            "] is not eligible for getting processed by all BeanPostProcessors " +
                            "(for example: not eligible for auto-proxying)");
                }
            }
            return bean;
        }

Seeing this message, the normal practice is to let

!(bean instanceof BeanPostProcessor) && !isInfrastructureBean(beanName) &&
                    this.beanFactory.getBeanPostProcessorCount() < this.beanPostProcessorTargetCount

This statement block is false. From the code, we can easily see that the statement block is false. There are several entries

  1. !(bean instanceof BeanPostProcessor)
  2. !isInfrastructureBean(beanName)
  3. this.beanFactory.getBeanPostProcessorCount() <
    this.beanPostProcessorTargetCount

1 and 3 do not seem to have much room to play, we can look at the code block of 2, 2 as follows

    private boolean isInfrastructureBean(@Nullable String beanName) {
            if (beanName != null && this.beanFactory.containsBeanDefinition(beanName)) {
                BeanDefinition bd = this.beanFactory.getBeanDefinition(beanName);
                return (bd.getRole() == RootBeanDefinition.ROLE_INFRASTRUCTURE);
            }
            return false;
        }
    }

From the code we can see

bd.getRole() == RootBeanDefinition.ROLE_INFRASTRUCTURE

This sentence is the core, but what the hell is ROLE_INFRASTRUCTURE, we continue to follow

    /**
     * Role hint indicating that a {@code BeanDefinition} is providing an
     * entirely background role and has no relevance to the end-user. This hint is
     * used when registering beans that are completely part of the internal workings
     * of a {@link org.springframework.beans.factory.parsing.ComponentDefinition}.
     */
    int ROLE_INFRASTRUCTURE = 2;

The meaning of this sentence is that when this role is declared, the bean does not belong to an external user, but belongs to spring. In other words, this bean is an official bean, not a mass bean. In short, this is an identity, so when we inject the bean into the spring container, we can do the following processing

    @Bean
    @Role(RootBeanDefinition.ROLE_INFRASTRUCTURE)
    public InterceptorHandler interceptorHandler(final ObjectProvider<List<Interceptor>> listObjectProvider) {
        return new InterceptorHandler(interceptorChain(listObjectProvider));
    }

When add

 @Role(RootBeanDefinition.ROLE_INFRASTRUCTURE)

After this comment, the world is really clean, and the console never appears again

not eligible for getting processed by all BeanPostProcessors
But is the problem really solved?

The answer is different. Many times we are easy to deceive a person, but it is difficult to deceive this person. For example, if you tell a girl that my family is rich, the girl believes it, and when the girl asks you to buy some valuable things for her, we find you. Can’t afford it. In order to please your sister, you must not use a swollen face to fill a fat man, and consume things that you simply can’t afford in advance, so that you can’t consume other things that you could consume later.

This is also true in the spring world. BeanPostProcessor itself is also a Bean. Generally speaking, its instantiation time is earlier than ordinary Beans, but she now has some requirements for the beans you implement, that is, BeanPostProcessor sometimes depends on some Beans, which leads to The instantiation of some ordinary Beans is earlier than the possible situation of BeanPostProcessor, which leads to some situations, for example, these pre-initialized beans cannot enjoy the extended functions of some post-processors.

So for the case of this article, use

 @Role(RootBeanDefinition.ROLE_INFRASTRUCTURE)

In fact, it does not solve the essence of the problem, but because the interceptor in this article does not require some subsequent spring features, this solution is also considered a kind of solution.

Is there any other solution? Yes, we can use

org.springframework.beans.factory.SmartInitializingSingleton

This class, he has a

afterSingletonsInstantiated()

Method, the function of this method is the callback interface after all the singleton beans are initialized and called. The example later in this article is to use this interface instead, the code is as follows

public class SpiInstanceInitializingSingleton implements SmartInitializingSingleton,BeanFactoryAware {

    private DefaultListableBeanFactory beanFactory;

    private InterceptorHandler interceptorHandler;

    public SpiInstanceInitializingSingleton(InterceptorHandler interceptorHandler) {
        this.interceptorHandler = interceptorHandler;
    }

    @Override
    public void afterSingletonsInstantiated() {
        changeBeanInstance2ProxyInstance();

    }
    }

Of course, you can also use spring's monitoring mechanism, such as monitoring refresh events for processing

Summarize

This article is an of how 1619309aab36ca custom-implemented SPI can be integrated with spring

demo link

https://github.com/lyb-geek/springboot-learning/tree/master/springboot-spi-enhance/springboot-spi-framework-spring


linyb极客之路
336 声望193 粉丝