前言
上一篇文章我們聊了一下自定義實現的SPI如何與spring進行整合,但其實在實現的過程中有個小細節,就是原先我們的SPI是帶了攔截器功能,(ps:對如何實現一個帶攔截器的SPI感興趣的朋友,可以檢視這篇文章-->聊聊如何實現一個帶有攔截器功能的SPI)。
為了保留這個攔截器功能,我原先的想法是狸貓換太子,在spring提供的後置處理器裡面,把攔截器功能給整合進去,當時的實現程式碼如下
@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;
}
}
功能是實現了,但是控制檯卻出現如下資訊
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)
排查過程
當時排查是通過控制提示的資訊,找到對應原始碼。在
org.springframework.context.support.PostProcessorRegistrationDelegate
找到相應的實現
@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;
}
看到這個資訊,按正常做法是讓
!(bean instanceof BeanPostProcessor) && !isInfrastructureBean(beanName) &&
this.beanFactory.getBeanPostProcessorCount() < this.beanPostProcessorTargetCount
這個語句塊為false,從程式碼我們很容易看出語句塊為false,有幾個入口
- !(bean instanceof BeanPostProcessor)
- !isInfrastructureBean(beanName)
- this.beanFactory.getBeanPostProcessorCount() <
this.beanPostProcessorTargetCount
1和3看起來是沒多大發揮空間,我們可以看下2,2的程式碼塊如下
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;
}
}
從程式碼我們可以看出
bd.getRole() == RootBeanDefinition.ROLE_INFRASTRUCTURE
這句話是核心,然而ROLE_INFRASTRUCTURE這個又是什麼鬼,我們繼續跟蹤
/**
* 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;
這句話表達的意思是當宣告這個角色時,這個bean就不是歸屬外部使用者,而是歸屬spring內部。換句話說就是這個bean是個官方bean,不是群眾bean。總之這個就是一個身份標識,因此我們在把bean注入到spring容器中,就可以做如下處理
@Bean
@Role(RootBeanDefinition.ROLE_INFRASTRUCTURE)
public InterceptorHandler interceptorHandler(final ObjectProvider<List<Interceptor>> listObjectProvider) {
return new InterceptorHandler(interceptorChain(listObjectProvider));
}
當加上
@Role(RootBeanDefinition.ROLE_INFRASTRUCTURE)
這個註解後,世界果然清淨了,控制檯再也沒有出現
not eligible for getting processed by all BeanPostProcessors
但問題真的就解決了嗎?
答案見仁見智,很多時候我們很容易欺騙一個人一時,但卻很難欺騙這個人一世,好比如你告訴妹子我家財萬貫,妹子相信了,等到妹子要你給她買一些貴重的東西時,發現你買不起,你為了討妹子歡心,不得以打腫臉充胖子,提前消費你壓根買不起的東西,導致你後面沒法再消費其他你本可以消費的東西
在spring世界也是如此,BeanPostProcessor本身也是一個Bean,一般而言其例項化時機要早過普通的Bean,但是她現在對於你實現的bean有一些需求,即BeanPostProcessor有時也會依賴一些Bean,這就導致了一些普通Bean的例項化早於BeanPostProcessor的可能情況,而引發一些情況,比如這些提前初始化的bean無法享有一些後置處理器擴充套件的功能
因此對本文的案例,用
@Role(RootBeanDefinition.ROLE_INFRASTRUCTURE)
其實是沒解決問題本質,但是因為本文的攔截器不需要後續的一些spring特色功能,因此這種解法也算是一種吧。
那有沒有其他解法,答案是有的,我們可以利用
org.springframework.beans.factory.SmartInitializingSingleton
這個類,他這個類裡面有個
afterSingletonsInstantiated()
方法,這個方法的作用是在所有單例bean初始化完成呼叫後的回撥介面。本文後面的例子就是改用這個介面實現,程式碼如下
public class SpiInstanceInitializingSingleton implements SmartInitializingSingleton,BeanFactoryAware {
private DefaultListableBeanFactory beanFactory;
private InterceptorHandler interceptorHandler;
public SpiInstanceInitializingSingleton(InterceptorHandler interceptorHandler) {
this.interceptorHandler = interceptorHandler;
}
@Override
public void afterSingletonsInstantiated() {
changeBeanInstance2ProxyInstance();
}
}
當然還可以使用spring的監聽機制,比如監聽refresh事件進行處理
總結
本文算是自定義實現的SPI如何與spring進行整合這篇文章的一點擴充套件補充