Spring源码系列-BeanPostProcessor与BeanFactoryPostProcessor

Ioc容器注册BeanPostProcessor后置处理器以及事件消息处理

前言

上一篇分析了BeanFactoryPostProcessor的作用,那么这一篇继续在refresh()方法里游荡,相信对Spring熟悉点的朋友,在看完BeanFactoryPostProcessor后,一定会想到Spring里面还有个BeanPostProcessor,那这个东西是什么作用呢?下面会进行介绍,同时由于注册BeanPostProcessor的逻辑比较简单,这里会穿插一下BeanPostProcessor生效的时机和源码逻辑,实际上这部分应该是Bean实例化出现的逻辑。

介绍完这部分之后,会介绍Spring的消息源初始化、广播器的初始以及监听器的初始化。这部分工作完成之后,Spring容器就会进入到Bean的创建过程,因为准备工作已经做得差不多了,容器已经准备好,接下来就是初始化Bean放进去容器里面。

BeanFactoryPostProcessor和BeanPostProcessor之间的区别

这两个的区别还是很显而易见的,主要表现在应用的阶段不同BeanFactoryPostProcessor是对BeanDefinition直接生效的,这更加底层,也更加原始,所以直接使用BeanFactoryPostProcessor会比较少。BeanPostProcessor是对bean实例生效的,相对于对BeanDefinition的处理,这个阶段更加靠后,BeanFactoryPostProcessor阶段bean是尚未初始化出来的,BeanPostProcessor处理的时候已经生成了实例对象,BeanPostProcessor会在对象的实例基础上进行一个更进一步的加工。

不熟悉的朋友看起来可能有点抽象,那么这里举一个例子吧。

BeanFactoryPostProcessor的类比:

假设你要造一个杯子,那么杯子需要一份原材料列表,材质你可以选择铁、铜、金、银等等,样式你可以选择圆型、方形、椭圆等等。假设开始原料选择铁,形状为圆形,那么这一份原料列表对应的就是一个BeanDefinition。原料列表出来后,没什么问题就会按照这一份列表去创建一个杯子。但是有时候需要一些额外的操作,例如对某些BeanDefinition进行检查,假设有一个检查员BeanFactoryPostProcessor去检查每个BeanDefinition。他看到杯子的材质是铁,觉得有失身份,于是把材料改成了金子,于是后续再去创建杯子的时候,就是个金杯了。

BeanPostProcessor的类比:

BeanPostProcessor的处理阶段则要靠后,在上面杯子创建完成之后,才到了BeanPostProcessor出场。BeanPostProcessor会在实例的基础上进行一些加工,拿杯子来举例,上一个阶段拿到的是一个粗糙的杯子,这里会进行一些处理,例如给杯子加点花纹样式,给杯子抛光等等。**注意这些操作都是在一个已有的杯子上进行的,但是请注意,这不是绝对的。**BeanPostProcessor除了能对Bean进行深加工外,还能直接进行Bean替换,类比来说,就是换了个杯子,偷梁换柱。Spring Aop的功能就是这样实现的,把经过代理的Bean放了进去,替换了原有的Bean。

所以比较一下得出一个很明显的结论:

  • BeanFactoryPostProcessor对BeanDefinition生效

  • BeanPostProcessor对bean实例生效

源码分析

registerBeanPostProcessors(beanFactory)

话不多说,下面继续分析refresh()方法里面的子方法,上一篇分析到了第五个子方法,那这篇从第六个registerBeanPostProcessors(beanFactory)开始。

跟进代码,可以看到实现都委托给了PostProcessorRegistrationDelegate#registerBeanPostProcessors(beanFactory, this)方法。

/**
  * Instantiate and register all BeanPostProcessor beans,
  * respecting explicit order if given.
  * <p>Must be called before any instantiation of application beans.
  *
  * 实例化并注册所有 BeanPostProcessor bean,如果给定顺序,则按照顺序排序。
  * <p>必须在应用程序 bean 的任何实例化之前调用。
  */
 protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {
  PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this);
 }

继续跟进,这个方法的逻辑也是比较简单的,跟上篇的BeanFactoryPostProcessor注册类似,这里也会按照优先级去对BeanPostProcessor进行排序然后按顺序进行注册。都是些家常套路了,可以跟着注释去看一下。值得注意的是,这里会额外加入两个BeanPostProcessor,分别为BeanPostProcessorCheckerApplicationListenerDetectorBeanPostProcessorChecker主要是用来记录一些日志,ApplicationListenerDetector是用来检测实现了ApplicationListener但是getBeanNamesForType()没探测出来的漏网之鱼。这里的漏网之鱼可能是一些动态注册的bean或者一些内部类,这里再次获取后会放入到applicationListeners集合里。

public static void registerBeanPostProcessors(
   ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {

  // 获取容器中所有的 BeanPostProcessor
  String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);

  // Register BeanPostProcessorChecker that logs an info message when
  // a bean is created during BeanPostProcessor instantiation, i.e. when
  // a bean is not eligible for getting processed by all BeanPostProcessors.
  int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length;
  // 注册 BeanPostProcessorChecker,当 bean 不符合所有 BeanPostProcessor 处理的条件时,它会在 BeanPostProcessor 实例化期间创建 bean 时记录一条信息消息
  beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));

  // Separate between BeanPostProcessors that implement PriorityOrdered,
  // Ordered, and the rest.
  // 按照顺序分类区分 BeanPostProcessor
  List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
  List<BeanPostProcessor> internalPostProcessors = new ArrayList<>();
  List<String> orderedPostProcessorNames = new ArrayList<>();
  List<String> nonOrderedPostProcessorNames = new ArrayList<>();
  for (String ppName : postProcessorNames) {
   if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
    BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
    priorityOrderedPostProcessors.add(pp);
    if (pp instanceof MergedBeanDefinitionPostProcessor) {
     internalPostProcessors.add(pp);
    }
   }
   else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
    orderedPostProcessorNames.add(ppName);
   }
   else {
    nonOrderedPostProcessorNames.add(ppName);
   }
  }

  // First, register the BeanPostProcessors that implement PriorityOrdered.
  // 首先注册实现了 PriorityOrdered 接口的 BeanPostProcessor
  sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
  registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors);

  // Next, register the BeanPostProcessors that implement Ordered.
  // 其次注册实现了 Ordered 接口的 BeanPostProcessor
  List<BeanPostProcessor> orderedPostProcessors = new ArrayList<>(orderedPostProcessorNames.size());
  for (String ppName : orderedPostProcessorNames) {
   BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
   orderedPostProcessors.add(pp);
   if (pp instanceof MergedBeanDefinitionPostProcessor) {
    internalPostProcessors.add(pp);
   }
  }
  sortPostProcessors(orderedPostProcessors, beanFactory);
  registerBeanPostProcessors(beanFactory, orderedPostProcessors);

  // Now, register all regular BeanPostProcessors.
  // 现在到了注册没有实现上述接口的 BeanPostProcessor
  List<BeanPostProcessor> nonOrderedPostProcessors = new ArrayList<>(nonOrderedPostProcessorNames.size());
  for (String ppName : nonOrderedPostProcessorNames) {
   BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
   nonOrderedPostProcessors.add(pp);
   if (pp instanceof MergedBeanDefinitionPostProcessor) {
    internalPostProcessors.add(pp);
   }
  }
  registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors);

  // Finally, re-register all internal BeanPostProcessors.
  // 最后,重新注册所有内部 BeanPostProcessor MergedBeanDefinitionPostProcessor。
  sortPostProcessors(internalPostProcessors, beanFactory);
  registerBeanPostProcessors(beanFactory, internalPostProcessors);

  // Re-register post-processor for detecting inner beans as ApplicationListeners,
  // moving it to the end of the processor chain (for picking up proxies etc).
  // 重新注册用于将内部 bean 检测为 ApplicationListeners 的后处理器,将其移动到处理器链的末尾(用于拾取代理等)。
  beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext));
 }

initMessageSource()

接下来继续进行下一步的准备工作,初始化消息源。这里是默认使用了父类的消息源,如果没有就初始化一个DelegatingMessageSource,这个DelegatingMessageSource会默认将所有的调用都委派到父容器的消息源去解析,如果没有父容器的消息源,那么它不会解析任何消息。

/**
  * Initialize the MessageSource.
  * Use parent's if none defined in this context.
  * 初始化消息源。如果没有在此上下文中定义,则使用父容器的。
  */
 protected void initMessageSource() {
  ConfigurableListableBeanFactory beanFactory = getBeanFactory();
  // 返回当前容器职工是否存在 messageSource,忽略祖先容器
  if (beanFactory.containsLocalBean(MESSAGE_SOURCE_BEAN_NAME)) {
   this.messageSource = beanFactory.getBean(MESSAGE_SOURCE_BEAN_NAME, MessageSource.class);
   // Make MessageSource aware of parent MessageSource.
   // 如果存在祖先,并且 messageSource 类型是 HierarchicalMessageSource,则获取祖先的 messageSource 设置到当前 messageSource 里。
   if (this.parent != null && this.messageSource instanceof HierarchicalMessageSource) {
    HierarchicalMessageSource hms = (HierarchicalMessageSource) this.messageSource;
    if (hms.getParentMessageSource() == null) {
     // Only set parent context as parent MessageSource if no parent MessageSource
     // registered already.
     hms.setParentMessageSource(getInternalParentMessageSource());
    }
   }
   if (logger.isTraceEnabled()) {
    logger.trace("Using MessageSource [" + this.messageSource + "]");
   }
  }
  else {
   // Use empty MessageSource to be able to accept getMessage calls.
   // 本地不存在 messageSource,使用空 MessageSource 能够接受 getMessage 调用
   DelegatingMessageSource dms = new DelegatingMessageSource();
   dms.setParentMessageSource(getInternalParentMessageSource());
   this.messageSource = dms;
   beanFactory.registerSingleton(MESSAGE_SOURCE_BEAN_NAME, this.messageSource);
   if (logger.isTraceEnabled()) {
    logger.trace("No '" + MESSAGE_SOURCE_BEAN_NAME + "' bean, using [" + this.messageSource + "]");
   }
  }
 }

initApplicationEventMulticaster()

初始化 ApplicationEventMulticaster,如果上下文中没有定义,则使用 SimpleApplicationEventMulticaster

/**
  * Initialize the ApplicationEventMulticaster.
  * Uses SimpleApplicationEventMulticaster if none defined in the context.
  *
  * 初始化 ApplicationEventMulticaster。
  * 如果上下文中没有定义,则使用 SimpleApplicationEventMulticaster。
  *
  * @see org.springframework.context.event.SimpleApplicationEventMulticaster
  */
 protected void initApplicationEventMulticaster() {
  ConfigurableListableBeanFactory beanFactory = getBeanFactory();
  // 如果本地容器里存在 applicationEventMulticaster,直接使用本地容器里的 applicationEventMulticaster
  if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
   this.applicationEventMulticaster =
     beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
   if (logger.isTraceEnabled()) {
    logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
   }
  }
  else {
   // 否则使用 SimpleApplicationEventMulticaster 广播器
   this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
   // 将给定的单例对象添加到该工厂的单例缓存中
   beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
   if (logger.isTraceEnabled()) {
    logger.trace("No '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME + "' bean, using " +
      "[" + this.applicationEventMulticaster.getClass().getSimpleName() + "]");
   }
  }
 }

这个事件广播器是干什么的呢?其实很简单,就是把一个事件广播到所有的ApplicationListener上。可以看一下里面的关键方法SimpleApplicationEventMulticaster#multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType),这里就是获取所有的listener,如果有异步线程池,则异步执行,否则逐个调用。

@Override
 public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
  // 解析事件的类型,这个type会用于后续的 ListenerCacheKey 缓存 key 构建
  ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
  // 获取线程池
  Executor executor = getTaskExecutor();
  // 逐个广播事件到 listener,就是将 listener 都遍历调用一遍
  for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
   if (executor != null) {
    executor.execute(() -> invokeListener(listener, event));
   }
   else {
    invokeListener(listener, event);
   }
  }
 }

 protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
  // 错误处理器,记录任务处理期间发生的错误
  ErrorHandler errorHandler = getErrorHandler();
  if (errorHandler != null) {
   try {
    doInvokeListener(listener, event);
   }
   catch (Throwable err) {
    errorHandler.handleError(err);
   }
  }
  else {
   doInvokeListener(listener, event);
  }
 }

 private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
  try {
   // 将事件传入listener,完成事件的监听回调
   listener.onApplicationEvent(event);
  }
  catch (ClassCastException ex) {
   // ...
  }
 }

onRefresh()

这是个空方法,交给子类实现。这里可以用来初始化特定上下文子类中的其他特殊 bean,也是留出来的一个扩展口。

/**
  * Template method which can be overridden to add context-specific refresh work.
  * Called on initialization of special beans, before instantiation of singletons.
  *
  * 可以重写以添加特定于上下文的刷新工作的模板方法。在单例实例化之前调用特殊 bean 的初始化。
  * 
  * <p>This implementation is empty.
  * @throws BeansException in case of errors
  * @see #refresh()
  */
 protected void onRefresh() throws BeansException {
  // For subclasses: do nothing by default.
 }

registerListeners()

上一步已经初始化完成了广播器,那接下来就是检查侦听器并注册它们。

事件可以按照注册的类型进行区分,可以分为以下三种:

  • 通过addApplicationListener()手动添加进去的

  • 容器里实现了ApplicationListener接口的

  • 容器启动早期需要的事件earlyApplicationEvents,早期事件是需要在这里直接发布的

/**
  * Add beans that implement ApplicationListener as listeners.
  * Doesn't affect other listeners, which can be added without being beans.
  *
  * 添加实现 ApplicationListener 的 bean作为侦听器。
  * 不影响其他监听器,可以添加而不是 bean。
  */
 protected void registerListeners() {
  // Register statically specified listeners first.
  // 首先注册静态指定的监听器,也就是通过addApplicationListener(ApplicationListener<?> listener) 注册的listener。
  for (ApplicationListener<?> listener : getApplicationListeners()) {
   getApplicationEventMulticaster().addApplicationListener(listener);
  }

  // Do not initialize FactoryBeans here: We need to leave all regular beans
  // uninitialized to let post-processors apply to them!
  // 这里只是获取beanName,是为了避免初始化 bean 导致后置处理器失效
  String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
  // 逐个注册listener
  for (String listenerBeanName : listenerBeanNames) {
   getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
  }

  // Publish early application events now that we finally have a multicaster...
  // 发布早期应用程序事件,因为我们终于有了一个广播器......
  // 忍辱负重,早期事件存到了这里才能进行发布,因为之前没有广播器
  Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
  this.earlyApplicationEvents = null;
  if (earlyEventsToProcess != null) {
   // 逐个发布事件
   for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
    getApplicationEventMulticaster().multicastEvent(earlyEvent);
   }
  }
 }

finishBeanFactoryInitialization(beanFactory)

准备工作已经基本完成,接下来就到了finishBeanFactoryInitialization(beanFactory)方法了。从方法名可以看到,这个方法是负责完成此上下文的 bean 工厂的初始化,初始化所有剩余的单例 bean。可以看到这个方法开始也进行了一些准备工作,例如注册类型装换器、占位符处理器以及LoadTimeWeaverAware加载等。最后会调用beanFactory.preInstantiateSingletons()进行对象创建,由于这里是比较复杂的过程,会分几篇文章去详细分析,这篇文章就是大概从表面上走完refresh()方法的源码。

/**
  * Finish the initialization of this context's bean factory,
  * initializing all remaining singleton beans.
  *
  * 完成此上下文的 bean 工厂的初始化,初始化所有剩余的单例 bean。
  * 
  */
 protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
  // Initialize conversion service for this context.
  // 初始化一个ConversionService用于类型转换,这个ConversionService会在实例化对象的时候用到
  if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
    beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
   beanFactory.setConversionService(
     beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
  }

  // Register a default embedded value resolver if no bean post-processor
  // (such as a PropertyPlaceholderConfigurer bean) registered any before:
  // at this point, primarily for resolution in annotation attribute values.
  // 添加一个StringValueResolver,用于处理占位符,可以看到,默认情况下就是使用环境中的属性值来替代占位符中的属性
  if (!beanFactory.hasEmbeddedValueResolver()) {
   beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
  }

  // Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
  // 创建所有的LoadTimeWeaverAware
  String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
  for (String weaverAwareName : weaverAwareNames) {
   getBean(weaverAwareName);
  }

  // Stop using the temporary ClassLoader for type matching.
  // 静态织入完成后将临时的类加载器设置为null,所以除了创建LoadTimeWeaverAware时可能会用到临时类加载器,其余情况下都为空
  beanFactory.setTempClassLoader(null);

  // Allow for caching all bean definition metadata, not expecting further changes.
  // 将所有的配置信息冻结
  beanFactory.freezeConfiguration();

  // Instantiate all remaining (non-lazy-init) singletons.
  // 开始进行真正的创建
  beanFactory.preInstantiateSingletons();
 }

finishRefresh()

到这里容器已经准备好了,bean也已经实例化完成,就差最后的一些事件通知和后续的兜底处理。这里比较重要的是会调用到所有实现了LifecycleProcessor#onRefresh()的Bean,在这里可以让生命周期Bean实现很多扩展。其次比较重要的是会发布一个ContextRefreshedEvent事件,通知所有监听器容器已经启动完成,这里就可以实现一些容器启动完成后的回调或者是一些任务等,任君发挥。

/**
  * Finish the refresh of this context, invoking the LifecycleProcessor's
  * onRefresh() method and publishing the
  * {@link org.springframework.context.event.ContextRefreshedEvent}.
  *
  * 完成容器的刷新启动,调用所有 LifecycleProcessor#onRefresh() 方法来发布 ContextRefreshedEvent 事件
  *
  */
 protected void finishRefresh() {
  // Clear context-level resource caches (such as ASM metadata from scanning).
  // 清除容器上下文级别的资源缓存(例如ASM扫描的元数据)
  clearResourceCaches();

  // Initialize lifecycle processor for this context.
  // 初始化上下文的 lifecycle processor
  initLifecycleProcessor();

  // Propagate refresh to lifecycle processor first.
  // 首先将刷新传播到生命周期处理器。
  getLifecycleProcessor().onRefresh();

  // Publish the final event.
  // 发布最终事件。
  publishEvent(new ContextRefreshedEvent(this));

  // Participate in LiveBeansView MBean, if active.
  // 参与 LiveBeansView MBean(如果处于活动状态)。
  LiveBeansView.registerApplicationContext(this);
 }

总结

本文的重点有点分散,更像是走马观花,但是分散里的重点毫无疑问是理解和区分BeanFactoryPostProcessorBeanPostProcessor之间的区别,文章开头通过一个例子去类比了一下这二者的作用阶段和分别可以完成什么工作,个人觉得还是比较贴切的,希望能够帮助到理解。

到这里已经基本把refresh()方法走了一遍,当然这里看到的大部分都是一些基础准备工作,最关键的Bean实例化是还没有开始分析的,Bean的实例化会后续分好几篇文章继续去分析。

Ioc容器BeanFactoryPostProcessor后置处理器分析

前言

上一篇文章Spring Ioc源码分析系列--Ioc源码入口分析已经介绍到Ioc容器的入口refresh()方法,并且分析了refresh()方法里面的前三个子方法分析了一下。还记得分析了什么麽?估计早忘了分析了什么,可以说是看了个寂寞。但是不要慌,看了忘肯定是正常的,需要回顾复习一下,最好做点笔记记录一下,有自己的沉淀才会印象深刻。最好跟着代码自己调试几遍,纸上得来终觉浅,绝知此事要躬行

好了,这里回顾一下上篇文章的内容。上篇文章主要分析了三个方法,prepareRefresh()进行了一些容器启动前的属性设置,obtainFreshBeanFactory()方法完成了读取配置文件,该方法的实现会针对xml配置创建内部容器,该容器负责bean的创建与管理,会进行BeanDefinition的注册,prepareBeanFactory(beanFactory)方法主要注册一些容器中需要使用的系统bean,例如classloaderBeanFactoryPostProcessor等。

喝了鸡汤看了回顾,接下来才是今天这篇文章的正文开始。

思考一个问题,Spring提供了非常良好的扩展性,那么扩展性在哪里体现?这个问题仁者见仁智者见智,但是把场景压缩一下,压缩到上一篇文章我们已经获得了一个加载完成BeanDefinition的容器上,我在统一的BeanDefinition加载完成后,我想修改某一个或者增加某一个BeanDefinition这时候怎么实现呢?

对Spring熟悉点的读者可能已经猜到,这时候就可以使用BeanFactoryPostProcessor后置处理器来完成这个操作了,那么这篇文章会解决两个疑问:

  • BeanFactoryPostProcessor是什么以及如何使用?

  • BeanFactoryPostProcessor在源码里的调用逻辑?

第一个疑问会通过一个例子来说明,第二个疑问会通过源码分析来阐述。废话少说,先进行BeanFactoryPostProcessor介绍,然后还是使用上一篇文章的例子来实现用BeanFactoryPostProcessor来替换UserService的实现。

BeanFactoryPostProcessor介绍

这是一个容器的钩子方法,允许自定义修改应用程序上下文的 bean 定义,调整上下文底层 bean 工厂的 bean 属性值。对于针对用系统管理员的自定义配置文件覆盖应用程序上下文中配置的 bean 属性非常有效。BeanFactoryPostProcessor可以与 bean 定义交互和修改,但不能与 bean 实例交互。这样做可能会导致过早的 bean 实例化,违反容器并导致意外的副作用。如果需要 bean 实例交互,请考虑改为实现 BeanPostProcessorApplicationContext 自动检测其 bean 定义中的 BeanFactoryPostProcessor ,并在创建任何其他 bean 之前应用它们。BeanFactoryPostProcessor 也可以通过编程方式注册到 ConfigurableApplicationContext

这个类只有一个方法,入参为当前容器,下面来看一下postProcessBeanFactory()这个方法。

@FunctionalInterface
public interface BeanFactoryPostProcessor {

 /**
  * Modify the application context's internal bean factory after its standard
  * initialization. All bean definitions will have been loaded, but no beans
  * will have been instantiated yet. This allows for overriding or adding
  * properties even to eager-initializing beans.
  *
  * 在标准初始化之后修改应用程序上下文的内部 bean 工厂。
  * 所有 bean 定义都将被加载,但还没有 bean 被实例化。
  * 这允许覆盖或添加属性,甚至是急切初始化的 bean。
  *
  * @param beanFactory the bean factory used by the application context
  * @throws org.springframework.beans.BeansException in case of errors
  */
 void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;

}

可以看到这里的操作空间是非常大的,直接把当前的容器传入,意味着你可以在当前容器的基础上做任何操作。就比如你天天看着女神,可望而不可即,但是某天通过一个传送门,把女神送到了你家里,你是不是可以为所欲为了,想干什么就干什么,例如你可以让她穿你喜欢的衣服,吃你喜欢吃的东西等等。

代码例子

那下面就用代码实现一下。还是在上一篇文章Spring Ioc源码分析系列--Ioc源码入口分析的基础上进行添加代码, 所有源码都可以在我的‍仓库ioc-sourcecode-analysis-code-demo里找到 。

首先新建一个ReplaceUserServiceImpl类实现UserService

/**
 * @author Codegitz
 * @date 2022/5/12 15:57
 **/
public class ReplaceUserServiceImpl implements UserService {
 @Override
 public User getUser(String name, String age) {
  User user = new User();
  user.setId("1");
  // 这里更改了赋值
  user.setName("ReplaceUser-" + name);
  user.setAge(age);
  return user;
 }
}

接着用一个类实现BeanFactoryPostProcessor。可以看到我这里的逻辑也非常简单,就是判断是否存在一个名为userServiceBeanDefinition,如果有则把它的实现替换为io.codegitz.service.impl.ReplaceUserServiceImpl

/**
 * @author Codegitz
 * @date 2022/5/12 16:01
 **/
public class ReplaceUserBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
 @Override
 public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
  if (beanFactory.containsBeanDefinition("userService")){
   BeanDefinition beanDefinition = beanFactory.getBeanDefinition("userService");
   beanDefinition.setBeanClassName("io.codegitz.service.impl.ReplaceUserServiceImpl");
  }
 }
}

后置处理器已经准备好,接下来把后置处理器注册到容器里,我代码例子采用的是xml方式。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

 <bean id="userService" class="io.codegitz.service.impl.UserServiceImpl"/>
 <!-- 注册后置处理器 -->
 <bean id="processor" class="io.codegitz.processor.ReplaceUserBeanFactoryPostProcessor"/>
</beans>

到这里一切都完成了,那么就可以启动引导类跑一下看看效果了。可以看到这里的实现已经替换为ReplaceUserServiceImpl,说明我们的ReplaceUserBeanFactoryPostProcessor后置处理器是生效了,那么是怎么生效的呢?进入下一节的源码分析。

源码分析

上面已经通过一个例子实现了功能,那么这个源码里是怎么实现的呢?这里会衔接上一篇文章的源码分析,继续在refresh()方法里游荡。上一篇已经分析了前三个,这篇会继续往下分析两个方法,分别是postProcessBeanFactory(beanFactory)invokeBeanFactoryPostProcessors(beanFactory)invokeBeanFactoryPostProcessors(beanFactory)就是后置处理器的逻辑,衔接了上文的例子。好家伙,互相衔接。

postProcessBeanFactory(beanFactory)

这是一个空方法,留给子类实现,主要是一些web相关的ApplicationContext会重写这个方法,传入的是当前容器,操作空间也是非常大的。由于是个空方法,这里不再赘述。

/**
  * Modify the application context's internal bean factory after its standard
  * initialization. All bean definitions will have been loaded, but no beans
  * will have been instantiated yet. This allows for registering special
  * BeanPostProcessors etc in certain ApplicationContext implementations.
  *
  * 在标准初始化之后修改应用程序上下文的内部 bean 工厂。
  * 所有 bean 定义都已经被加载,但还没有 bean 被实例化。
  * 这允许在某些 ApplicationContext 实现中注册特殊的 BeanPostProcessors 等。
  *
  * @param beanFactory the bean factory used by the application context
  */
 protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
 }

invokeBeanFactoryPostProcessors(beanFactory)

接下来就进入了今天的重头戏,跟进代码,可以看到主要的代码实现委托给了PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors())去实现。

/**
  * Instantiate and invoke all registered BeanFactoryPostProcessor beans,
  * respecting explicit order if given.
  * <p>Must be called before singleton instantiation.
  *
  * 实例化并调用所有已注册的 BeanFactoryPostProcessor bean,如果给定顺序,则按照顺序去执行
  * 所有的后置处理器必须在其他的单例bean实例化之前被调用
  */
 //实例化并且调用所有BeanFactoryPostProcessor beans
 protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
  // 这个方法会进行两种操作
  // 1.把给定的 BeanFactoryPostProcessor 传入执行
  // 2.自动扫描容器里的所有的 BeanFactoryPostProcessor 执行
  PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());

  // Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime
  // (e.g. through an @Bean method registered by ConfigurationClassPostProcessor)
  // 检测是否有用于类型匹配的临时 ClassLoader 和 LoadTimeWeaver
  if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
   beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
   beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
  }
 }

跟进PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors())里面的代码。这个方法比较长,但是逻辑却非常简单,跟着注释看一下理解起来问题不大。

public static void invokeBeanFactoryPostProcessors(
   ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {

  // Invoke BeanDefinitionRegistryPostProcessors first, if any.
  Set<String> processedBeans = new HashSet<>();

  if (beanFactory instanceof BeanDefinitionRegistry) {
   BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
   // 常规的BeanFactoryPostProcessor后置处理器
   List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
   // 扩展注册BeanDefinition的BeanDefinitionRegistryPostProcessor后置处理器
   List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();

   // 根据类型放入不同的后置处理器列表里
   for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
    if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
     BeanDefinitionRegistryPostProcessor registryProcessor =
       (BeanDefinitionRegistryPostProcessor) postProcessor;
     registryProcessor.postProcessBeanDefinitionRegistry(registry);
     registryProcessors.add(registryProcessor);
    }
    else {
     regularPostProcessors.add(postProcessor);
    }
   }

   // Do not initialize FactoryBeans here: We need to leave all regular beans
   // uninitialized to let the bean factory post-processors apply to them!
   // Separate between BeanDefinitionRegistryPostProcessors that implement
   // PriorityOrdered, Ordered, and the rest.
   // 这里不会初始化FactoryBeans,因为先初始化了的话BeanFactoryPostProcessor就无法对已经初始化的bean生效了
   List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();

   // First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered.
   // 首先会调用实现了PriorityOrdered接口的BeanDefinitionRegistryPostProcessors
   String[] postProcessorNames =
     beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
   for (String ppName : postProcessorNames) {
    if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
     currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
     processedBeans.add(ppName);
    }
   }
   sortPostProcessors(currentRegistryProcessors, beanFactory);
   registryProcessors.addAll(currentRegistryProcessors);
   invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
   currentRegistryProcessors.clear();

   // Next, invoke the BeanDefinitionRegistryPostProcessors that implement Ordered.
   // 接下来调用实现了Ordered接口的BeanDefinitionRegistryPostProcessors
   postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
   for (String ppName : postProcessorNames) {
    if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
     currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
     processedBeans.add(ppName);
    }
   }
   sortPostProcessors(currentRegistryProcessors, beanFactory);
   registryProcessors.addAll(currentRegistryProcessors);
   invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
   currentRegistryProcessors.clear();

   // Finally, invoke all other BeanDefinitionRegistryPostProcessors until no further ones appear.
   // 最后,调用没有实现上述接口的BeanDefinitionRegistryPostProcessors
   boolean reiterate = true;
   while (reiterate) {
    reiterate = false;
    postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
    for (String ppName : postProcessorNames) {
     if (!processedBeans.contains(ppName)) {
      currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
      processedBeans.add(ppName);
      // 为啥这里要再次把reiterate设置为true?
      // 因为这里BeanDefinitionRegistryPostProcessor可能会注册另外的BeanFactoryPostProcessor,
      // 所以需要循环去迭代,直到当前容器里没有BeanFactoryPostProcessor为止
      reiterate = true;
     }
    }
    sortPostProcessors(currentRegistryProcessors, beanFactory);
    registryProcessors.addAll(currentRegistryProcessors);
    // 这里可能会继续注册BeanFactoryPostProcessor
    invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
    currentRegistryProcessors.clear();
   }

   // Now, invoke the postProcessBeanFactory callback of all processors handled so far.
   // 前面已经处理完成了所有的 BeanDefinitionRegistryPostProcessor,
   // 接下来这两个方法就是处理 BeanFactoryPostProcessor,
   // 注意这里只是处理了方法传入的 beanFactoryPostProcessors ,
   // 处理完这个后后续的逻辑还是会自动检测当前容器里所有的BeanFactoryPostProcessor,然后分类逐次调用
   invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
   invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
  }

  else {
   // Invoke factory processors registered with the context instance.
   // 如果给定的beanFactory不是BeanDefinitionRegistry,
   // 那么就不需要进行前面调用 BeanDefinitionRegistryPostProcessor 的操作,
   // 直接调用给定的 beanFactoryPostProcessors
   invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory);
  }

  // Do not initialize FactoryBeans here: We need to leave all regular beans
  // uninitialized to let the bean factory post-processors apply to them!
  // 这里的逻辑跟上面的 BeanDefinitionRegistryPostProcessor 处理逻辑类似
  String[] postProcessorNames =
    beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);

  // Separate between BeanFactoryPostProcessors that implement PriorityOrdered,
  // Ordered, and the rest.
  // 按照不同的优先级去分类 BeanFactoryPostProcessor
  List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
  List<String> orderedPostProcessorNames = new ArrayList<>();
  List<String> nonOrderedPostProcessorNames = new ArrayList<>();
  for (String ppName : postProcessorNames) {
   // 如果第一阶段以及处理过了,不再处理
   if (processedBeans.contains(ppName)) {
    // skip - already processed in first phase above
   }
   else if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
    priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));
   }
   else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
    orderedPostProcessorNames.add(ppName);
   }
   else {
    nonOrderedPostProcessorNames.add(ppName);
   }
  }

  // First, invoke the BeanFactoryPostProcessors that implement PriorityOrdered.
  // 首先调用实现了 PriorityOrdered 接口的 BeanFactoryPostProcessor
  sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
  invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);

  // Next, invoke the BeanFactoryPostProcessors that implement Ordered.
  // 其次调用实现了 Ordered 接口的 BeanFactoryPostProcessor
  List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<>(orderedPostProcessorNames.size());
  for (String postProcessorName : orderedPostProcessorNames) {
   orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
  }
  sortPostProcessors(orderedPostProcessors, beanFactory);
  invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);

  // Finally, invoke all other BeanFactoryPostProcessors.
  // 最后调用没有实现上述接口的 BeanFactoryPostProcessor
  List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<>(nonOrderedPostProcessorNames.size());
  for (String postProcessorName : nonOrderedPostProcessorNames) {
   nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
  }
  invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);

  // Clear cached merged bean definitions since the post-processors might have
  // modified the original metadata, e.g. replacing placeholders in values...
  // 清除缓存的 merged bean definitions 定义,因为后处理器可能已经修改了原始元数据,例如替换值中的占位符...
  beanFactory.clearMetadataCache();
 }

这个方法主要做了以下几件事:

  • 首先会判断当前容器的类型是不是BeanDefinitionRegistry类型,如果是,则判断给定的beanFactoryPostProcessors是否存在BeanDefinitionRegistryPostProcessor类型的后置处理器,如果有则执行postProcessBeanDefinitionRegistry()方法,进行BeanDefinition的注册。接着会分别获取当前容器里实现了PriorityOrderedOrdered接口和没有实现排序接口的BeanDefinitionRegistryPostProcessor,排序后依次执行。

  • 如果不是BeanDefinitionRegistry类型,则直接调用invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory)方法调用所有给定的BeanFactoryPostProcessor

  • 经过前面两步,已经处理完成了给定了beanFactoryPostProcessors,这里会去检测执行容器里存在的BeanFactoryPostProcessor。这里也会按照PriorityOrderedOrdered接口以及没有实现排序接口的顺序去调用。

  • 最后会清空容器里元数据的配置缓存

这个方法的逻辑是比较简单的,就是代码量比较大,需要耐心看完。

接下来看下里面的一些字方法的内容

sortPostProcessors()完成后置处理器排序
private static void sortPostProcessors(List<?> postProcessors, ConfigurableListableBeanFactory beanFactory) {
  // 比较简单,就是获取一个比较器 Comparator ,至于 Comparator 的原理,可以自己去看下相关文章
  Comparator<Object> comparatorToUse = null;
  if (beanFactory instanceof DefaultListableBeanFactory) {
   comparatorToUse = ((DefaultListableBeanFactory) beanFactory).getDependencyComparator();
  }
  if (comparatorToUse == null) {
   comparatorToUse = OrderComparator.INSTANCE;
  }
  // 排序
  postProcessors.sort(comparatorToUse);
 }
invokeBeanDefinitionRegistryPostProcessors()调用所有的BeanDefinitionRegistryPostProcessor

这个方法也比较简单,就是逐个调用一下BeanDefinitionRegistryPostProcessor后置处理器。

/**
  * Invoke the given BeanDefinitionRegistryPostProcessor beans.
  * 调用给定的 BeanDefinitionRegistryPostProcessor bean。
  */
 private static void invokeBeanDefinitionRegistryPostProcessors(
   Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry) {

  for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {
   postProcessor.postProcessBeanDefinitionRegistry(registry);
  }
 }
invokeBeanFactoryPostProcessors()调用所有的BeanFactoryPostProcessor

逐个调用一下BeanFactoryPostProcessor后置处理器。

/**
  * Invoke the given BeanFactoryPostProcessor beans.
  * 调用给定的 BeanFactoryPostProcessor bean。
  */
 private static void invokeBeanFactoryPostProcessors(
   Collection<? extends BeanFactoryPostProcessor> postProcessors, ConfigurableListableBeanFactory beanFactory) {

  for (BeanFactoryPostProcessor postProcessor : postProcessors) {
   postProcessor.postProcessBeanFactory(beanFactory);
  }
 }
beanFactory.getBeanNamesForType()根据类型获取bean名称

beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false)会根据传入的类型获取该类型所有的bean名称。

跟进代码查看,会调用doGetBeanNamesForType()方法进行获取。

public String[] getBeanNamesForType(@Nullable Class<?> type, boolean includeNonSingletons, boolean allowEagerInit) {
  // 配置没有冻结 || 类型为空 || 不允许提前初始化 则进入
  if (!isConfigurationFrozen() || type == null || !allowEagerInit) {
   return doGetBeanNamesForType(ResolvableType.forRawClass(type), includeNonSingletons, allowEagerInit);
  }
  Map<Class<?>, String[]> cache =
    (includeNonSingletons ? this.allBeanNamesByType : this.singletonBeanNamesByType);
  String[] resolvedBeanNames = cache.get(type);
  if (resolvedBeanNames != null) {
   return resolvedBeanNames;
  }
  // 允许提前初始化
  resolvedBeanNames = doGetBeanNamesForType(ResolvableType.forRawClass(type), includeNonSingletons, true);
  if (ClassUtils.isCacheSafe(type, getBeanClassLoader())) {
   cache.put(type, resolvedBeanNames);
  }
  return resolvedBeanNames;
 }

跟进doGetBeanNamesForType()方法,这个方法也比较长,但是逻辑也比较清晰,就是遍历了beanDefinitionNames判断是否符合类型要求,符合则返回。

private String[] doGetBeanNamesForType(ResolvableType type, boolean includeNonSingletons, boolean allowEagerInit) {
  List<String> result = new ArrayList<>();

  // Check all bean definitions.
  // 从所有的 beanDefinitionNames 检查所有符合类型要求的 bean ,加入 result
  for (String beanName : this.beanDefinitionNames) {
   // Only consider bean as eligible if the bean name
   // is not defined as alias for some other bean.
   // 如果 bean 名称未定义为其他 bean 的别名,则该bean符合要求。
   if (!isAlias(beanName)) {
    try {
     // 获取 beanName 的 RootBeanDefinition
     RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
     // Only check bean definition if it is complete.
     if (!mbd.isAbstract() && (allowEagerInit ||
       (mbd.hasBeanClass() || !mbd.isLazyInit() || isAllowEagerClassLoading()) &&
         !requiresEagerInitForType(mbd.getFactoryBeanName()))) {
      boolean isFactoryBean = isFactoryBean(beanName, mbd);
      BeanDefinitionHolder dbd = mbd.getDecoratedDefinition();
      // 类型是否匹配
      boolean matchFound = false;
      boolean allowFactoryBeanInit = allowEagerInit || containsSingleton(beanName);
      boolean isNonLazyDecorated = dbd != null && !mbd.isLazyInit();
      // 主要就是调用 isTypeMatch() 方法判断类型是否匹配
      if (!isFactoryBean) {
       if (includeNonSingletons || isSingleton(beanName, mbd, dbd)) {
        matchFound = isTypeMatch(beanName, type, allowFactoryBeanInit);
       }
      }
      else  {
       if (includeNonSingletons || isNonLazyDecorated ||
         (allowFactoryBeanInit && isSingleton(beanName, mbd, dbd))) {
        matchFound = isTypeMatch(beanName, type, allowFactoryBeanInit);
       }
       if (!matchFound) {
        // In case of FactoryBean, try to match FactoryBean instance itself next.
        beanName = FACTORY_BEAN_PREFIX + beanName;
        matchFound = isTypeMatch(beanName, type, allowFactoryBeanInit);
       }
      }
      // 如果匹配则加入
      if (matchFound) {
       result.add(beanName);
      }
     }
    }
    catch (CannotLoadBeanClassException | BeanDefinitionStoreException ex) {
     // 省略部分异常处理..
    }
   }
  }


  // Check manually registered singletons too.
  // 检查手动注册的单例bean
  for (String beanName : this.manualSingletonNames) {
   try {
    // In case of FactoryBean, match object created by FactoryBean.
    if (isFactoryBean(beanName)) {
     if ((includeNonSingletons || isSingleton(beanName)) && isTypeMatch(beanName, type)) {
      result.add(beanName);
      // Match found for this bean: do not match FactoryBean itself anymore.
      continue;
     }
     // In case of FactoryBean, try to match FactoryBean itself next.
     beanName = FACTORY_BEAN_PREFIX + beanName;
    }
    // Match raw bean instance (might be raw FactoryBean).
    // 匹配则加入
    if (isTypeMatch(beanName, type)) {
     result.add(beanName);
    }
   }
   catch (NoSuchBeanDefinitionException ex) {
    // 省略部分异常...
   }
  }

  return StringUtils.toStringArray(result);
 }

到这里BeanFactoryPostProcessor的源码调用以及相关的逻辑分析完了,是不是比较简单。

小结

这篇文章主要是介绍了BeanFactoryPostProcessor后置处理器的使用和底层原理。文章开头首先回顾了上一篇文章的内容,然后例子驱动分析,先用简单的例子实现了BeanFactoryPostProcessor的使用,然后再进行了源码分析,整个文章下来思路是比较清晰的。那么看到这里,有没有解决了文章开头的两个疑问?

  • BeanFactoryPostProcessor是什么以及如何使用?

  • BeanFactoryPostProcessor在源码里的调用逻辑?

如果没有,那不怪你,可能是我写得不好。如果有人看到这里,那在这里老话重提。与君共勉,路漫漫其修远兮,吾将上下而求索。

ab851de5e97b3b06b0a5fd4e9956e615.gif

1.Mysql完结汇总篇(18W字送给大家),完结撒花

2.如何啃下JVM这座大山,完结撒花(完结篇)

3.最全的八股文线程池总结(臭不要脸)

4.手把手教新人调优

5.上班摸鱼学习法

6.阻塞队列yyds

7.线程与

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值