01
preface
In the last article, we talked about how to integrate the custom implemented SPI with spring, but in fact, there is a small detail in the implementation process, that is, our spi was originally equipped with interceptor function, (ps: friends interested in how to implement an SPI with interceptor can view this article -- > Talk about how to implement an SPI with interceptor function).
In order to retain the interceptor function, my original idea was to change the crown prince and integrate the interceptor function in 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 implemented, but the following information 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)
02
Troubleshooting process
At that time, the troubleshooting was to find the corresponding source code by controlling the prompt information. stay
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; }
When you see this message, it's normal to let
!(bean instanceof BeanPostProcessor) && !isInfrastructureBean(beanName) && this.beanFactory.getBeanPostProcessorCount() < this.beanPostProcessorTargetCount
The statement block is false. We can easily see from the code that the statement block is false. There are several entries
- !(bean instanceof BeanPostProcessor)
- !isInfrastructureBean(beanName)
- this.beanFactory.getBeanPostProcessorCount() < this.beanPostProcessorTargetCount
1 and 3 don't seem to have much room to play. We can look at the code blocks of 2 and 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, however, role_ What the hell is this? We'll keep tracking
/** * 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;
This sentence means that when the role is declared, the bean does not belong to external users, but to spring. In other words, this bean is an official bean, not a mass bean. In short, this is an identity, so we can do the following when injecting beans into the spring container
@Bean @Role(RootBeanDefinition.ROLE_INFRASTRUCTURE) public InterceptorHandler interceptorHandler(final ObjectProvider<List<Interceptor>> listObjectProvider) { return new InterceptorHandler(interceptorChain(listObjectProvider)); }
When you add
@Role(RootBeanDefinition.ROLE_INFRASTRUCTURE)
After this annotation, the world was really clean, and the console never appeared again
not eligible for getting processed by all BeanPostProcessors
But is the problem really solved?
Different people have different opinions on the answer. In many cases, it is easy for us to deceive a person for a while, but it is difficult for us to deceive this person for a lifetime. For example, you tell your sister that my family is rich, and my sister believes it. When my sister asks you to buy her something valuable, you find that you can't afford it. In order to please my sister, you must not make a fat face and consume things you can't afford in advance, So you can't consume other things you could have consumed later
The same is true in the spring world. BeanPostProcessor itself is also a bean. Generally speaking, its instantiation time is earlier than that of ordinary beans, but now she has some requirements for the beans you implement, that is, BeanPostProcessor sometimes depends on some beans, which leads to the possibility that the instantiation of ordinary beans is earlier than that of BeanPostProcessor, causing some situations, For example, these pre initialized beans cannot enjoy some post processor extended functions
Therefore, for the case of this paper, use
@Role(RootBeanDefinition.ROLE_INFRASTRUCTURE)
In fact, it does not solve the essence of the problem, but because the interceptor in this paper does not need some subsequent spring features, this solution is also a kind of solution.
Is there any other solution? The answer is yes. We can use it
org.springframework.beans.factory.SmartInitializingSingleton
This class, he has one in this class
afterSingletonsInstantiated()
Method, which is used to call the callback interface after all singleton bean s are initialized. The example later in this article is to use this interface for implementation. 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 listening mechanism, such as listening to refresh events for processing
03
summary
This article is Talk about how to integrate SPI with spring An extension of this article
04
demo link
https://github.com/lyb-geek/springboot-learning/tree/master/springboot-spi-enhance/springboot-spi-framework-spring