|
|
51CTO旗下网站
|
|
移动端

聊聊容器的始祖 DefaultListableBeanFactory

DefaultListableBeanFactory 是一个完整的、功能成熟的 IoC 容器,如果你的需求很简单,甚至可以直接使用 DefaultListableBeanFactory,如果你的需求比较复杂,那么通过扩展 DefaultListableBeanFactory 的功能也可以达到,可以说 DefaultListableBeanFactory 是整个 Spring IoC 容器的始祖。

作者: 江南一点雨来源:江南一点雨|2020-07-14 07:27

 1.DefaultListableBeanFactory

要说 XmlBeanFactory 就不得不先说它的父类 DefaultListableBeanFactory,因为 XmlBeanFactory 中的大部分功能实际上在 DefaultListableBeanFactory 中就已经提供好了,XmlBeanFactory 只是对 IO 流的读取做了一些定制而已。

DefaultListableBeanFactory 是一个完整的、功能成熟的 IoC 容器,如果你的需求很简单,甚至可以直接使用 DefaultListableBeanFactory,如果你的需求比较复杂,那么通过扩展 DefaultListableBeanFactory 的功能也可以达到,可以说 DefaultListableBeanFactory 是整个 Spring IoC 容器的始祖。

我们先来看一下 DefaultListableBeanFactory 的继承关系:

从这张类的关系图中可以看出,DefaultListableBeanFactory 实际上也是一个集大成者。在 Spring 中,针对 Bean 的不同操作都有不同的接口进行规范,每个接口都有自己对应的实现,最终在 DefaultListableBeanFactory 中将所有的实现汇聚到一起。从这张类的继承关系图中我们大概就能感受到 Spring 中关于类的设计是多么厉害,代码耦合度非常低。

这些类,在本系列后面的介绍中,大部分都会涉及到,现在我先大概介绍一下每个类的作用,大家先混个脸熟:

  1. BeanFactory:这个接口看名字就知道是一个 Bean 的工厂,BeanFactory 接口定义了各种获取 Bean 的方法、判断 Bean 是否存在、判断 Bean 是否单例等针对 Bean 的基础方法。
  2. ListableBeanFactory:这个接口继承自 BeanFactory,在 BeanFactory 的基础上,扩展了 Bean 的查询方法,例如根据类型获取 BeanNames、根据注解获取 BeanNames、根据 Bean 获取注解等。
  3. AutowireCapableBeanFactory:该接口继承自 BeanFactory,在 BeanFactory 的基础上,提供了 Bean 的创建、配置、注入、销毁等操作。有时候我们需要自己手动注入 Bean 的时候,可以考虑通过实现该接口来完成。AutowireCapableBeanFactory 在 Spring Security 中有一个重要的应用就是 ObjectPostProcessor,这个松哥将在 ??Spring Security 系列中和大家详细介绍。
  4. HierarchicalBeanFactory:该接口继承自 BeanFactory,并在 BeanFactory 基础上添加了获取 parent beanfactory 的方法。
  5. SingletonBeanRegistry:这个接口定义了对单例 Bean 的定义以及获取方法。
  6. ConfigurableBeanFactory:这个接口主要定了针对 BeanFactory 的各种配置以及销毁的方法。
  7. ConfigurableListableBeanFactory:这是 BeanFactory 的配置清单,这里定义了忽略的类型、接口,通过 Bean 的名称获取 BeanDefinition 、冻结 BeanDefinition 等。
  8. AliasRegistry:这个接口定义了对 alias 的注册、移除、判断以及查询操作。
  9. SimpleAliasRegistry:这个类实现了 AliasRegistry 接口并实现了它里边的方法,SimpleAliasRegistry 使用 ConcurrentHashMap 做载体,实现了对 alias 的注册、移除判断以及查询操作。
  10. DefaultSingletonBeanRegistry:这个类基于 Java 中的集合,对 SingletonBeanRegistry 接口进行了实现。
  11. FactoryBeanRegistrySupport:该类继承自 DefaultSingletonBeanRegistry,并在 DefaultSingletonBeanRegistry 的基础上,增加了获取 FactoryBean 类型、移除 FactoryBean 缓存的方法等等操作。
  12. AbstractBeanFactory:实现了 ConfigurableBeanFactory 接口并继承自 FactoryBeanRegistrySupport,在 AbstractBeanFactory 中对 ConfigurableBeanFactory 中定义的方法进行了实现。
  13. AbstractAutowireCapableBeanFactory:该类继承自 AbstractBeanFactory 并对 AutowireCapableBeanFactory 接口中定义的方法进行了落地实现。
  14. BeanDefinitionRegistry:这个接口继承自 AliasRegistry 接口,并增加了一系列针对 BeanDefinition 的注册、移除、查询、判断等方法。
  15. 最后的 DefaultListableBeanFactory 自然就具备了上面所有的功能。

上面的内容可能看的大家眼花缭乱,松哥这里通过几个简单实际的例子,来带大家使用一下 DefaultListableBeanFactory 的功能,可能大家的理解就比较清晰了。

DefaultListableBeanFactory 作为一个集大成者,提供了非常多的功能,我们一个一个来看。

2.代码改造

首先文章中一开始的三行代码我们可以对其略加改造,因为我们已经说了 XmlBeanFactory 中的大部分功能实际上在 DefaultListableBeanFactory 中就已经提供好了,XmlBeanFactory 只是对 IO 流的读取做了一些定制而已,文件的读取主要是通过 XmlBeanDefinitionReader 来完成的(本系列前面文章已经讲过),我们可以对文章一开始的三行代码进行改造,以便更好的体现“XmlBeanFactory 中的大部分功能实际上在 DefaultListableBeanFactory 中就已经提供好了”:

  1. ClassPathResource res=new ClassPathResource("beans.xml"); 
  2. DefaultListableBeanFactory factory=new DefaultListableBeanFactory(); 
  3. XmlBeanDefinitionReader reader=new XmlBeanDefinitionReader(factory); 
  4. reader.loadBeanDefinitions(res); 
  5. User user = factory.getBean(User.class); 
  6. System.out.println("user = " + user); 

使用前四行代码代替 XmlBeanFactory,这样 XmlBeanFactory 的功能是不是就很明确了?就是前四行代码的功能。

3.动态注册 Bean

动态注册 Bean,这是 DefaultListableBeanFactory 的功能之一,不过准确来说应该是动态注册 BeanDefinition 。

我们先来看一个简单的例子:

  1. DefaultListableBeanFactory defaultListableBeanFactory = new DefaultListableBeanFactory(); 
  2. GenericBeanDefinition userBeanDefinition = new GenericBeanDefinition(); 
  3. MutablePropertyValues pvs = new MutablePropertyValues(); 
  4. pvs.add("username""javaboy"); 
  5. pvs.add("address""www.javaboy.org"); 
  6. userBeanDefinition.setPropertyValues(pvs); 
  7. userBeanDefinition.setBeanClass(User.class); 
  8. defaultListableBeanFactory.registerBeanDefinition("user", userBeanDefinition); 
  9. User user = defaultListableBeanFactory.getBean(User.class); 
  10. System.out.println("user = " + user); 

首先我们自己手动构建一个 DefaultListableBeanFactory 对象。当然也可以使用前面的 XmlBeanFactory。

然后再手动构建一个 GenericBeanDefinition。在前面的文章中,松哥和大家讲过,现在默认使用的 BeanDefinition 就是 GenericBeanDefinition,所以这里我们自己也手动构建一个 GenericBeanDefinition。有了 GenericBeanDefinition 之后,我们设置相关的类和属性。

接下来再将 userBeanDefinition 注册到 defaultListableBeanFactory。注册完成之后,我们就可以从 defaultListableBeanFactory 中获取相应的 Bean 了。

这里说一句题外话,希望大家在阅读本系列每一篇文章的时候,能够将本系列前后文章联系起来一起理解,这样会有很多意料之外的收获。例如上面的,我们既可以声明一个 DefaultListableBeanFactory,也可以声明一个 XmlBeanFactory,那你大概就能据此推断出 XmlBeanFactory 的主要目的可能就是对资源文件进行读取和注册。

那么到底是怎么注册的呢?我们来看一下 defaultListableBeanFactory.registerBeanDefinition 方法的定义:

  1. @Override 
  2. public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) 
  3.   throws BeanDefinitionStoreException { 
  4.  Assert.hasText(beanName, "Bean name must not be empty"); 
  5.  Assert.notNull(beanDefinition, "BeanDefinition must not be null"); 
  6.  if (beanDefinition instanceof AbstractBeanDefinition) { 
  7.   try { 
  8.    ((AbstractBeanDefinition) beanDefinition).validate(); 
  9.   } 
  10.   catch (BeanDefinitionValidationException ex) { 
  11.    throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, 
  12.      "Validation of bean definition failed", ex); 
  13.   } 
  14.  } 
  15.  BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName); 
  16.  if (existingDefinition != null) { 
  17.   if (!isAllowBeanDefinitionOverriding()) { 
  18.    throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition); 
  19.   } 
  20.   else if (existingDefinition.getRole() < beanDefinition.getRole()) { 
  21.    // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE 
  22.    if (logger.isInfoEnabled()) { 
  23.     logger.info("Overriding user-defined bean definition for bean '" + beanName + 
  24.       "' with a framework-generated bean definition: replacing [" + 
  25.       existingDefinition + "] with [" + beanDefinition + "]"); 
  26.    } 
  27.   } 
  28.   else if (!beanDefinition.equals(existingDefinition)) { 
  29.    if (logger.isDebugEnabled()) { 
  30.     logger.debug("Overriding bean definition for bean '" + beanName + 
  31.       "' with a different definition: replacing [" + existingDefinition + 
  32.       "] with [" + beanDefinition + "]"); 
  33.    } 
  34.   } 
  35.   else { 
  36.    if (logger.isTraceEnabled()) { 
  37.     logger.trace("Overriding bean definition for bean '" + beanName + 
  38.       "' with an equivalent definition: replacing [" + existingDefinition + 
  39.       "] with [" + beanDefinition + "]"); 
  40.    } 
  41.   } 
  42.   this.beanDefinitionMap.put(beanName, beanDefinition); 
  43.  } 
  44.  else { 
  45.   if (hasBeanCreationStarted()) { 
  46.    // Cannot modify startup-time collection elements anymore (for stable iteration) 
  47.    synchronized (this.beanDefinitionMap) { 
  48.     this.beanDefinitionMap.put(beanName, beanDefinition); 
  49.     List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1); 
  50.     updatedDefinitions.addAll(this.beanDefinitionNames); 
  51.     updatedDefinitions.add(beanName); 
  52.     this.beanDefinitionNames = updatedDefinitions; 
  53.     removeManualSingletonName(beanName); 
  54.    } 
  55.   } 
  56.   else { 
  57.    // Still in startup registration phase 
  58.    this.beanDefinitionMap.put(beanName, beanDefinition); 
  59.    this.beanDefinitionNames.add(beanName); 
  60.    removeManualSingletonName(beanName); 
  61.   } 
  62.   this.frozenBeanDefinitionNames = null
  63.  } 
  64.  if (existingDefinition != null || containsSingleton(beanName)) { 
  65.   resetBeanDefinition(beanName); 
  66.  } 
  67.  else if (isConfigurationFrozen()) { 
  68.   clearByTypeCache(); 
  69.  } 

registerBeanDefinition 方法是在 BeanDefinitionRegistry 接口中声明的,DefaultListableBeanFactory 类实现了 BeanDefinitionRegistry 接口,并实现了该方法,我们来看分析下该方法:

  • 首先对传入的 beanDefinition 对象进行校验,这也是注册前的最后一次校验,不过这个时候 BeanDefinition 对象已经到手了,所以这个校验并非 XML 文件校验,这里主要是对 methodOverrides 的校验。
  • 接下来会根据 beanName 从 beanDefinitionMap 中获取 BeanDefinition,看看当前 Bean 是否已经定义过了。beanDefinitionMap 是一个 Map 集合,这个集合中 key 是 beanName,value 是 BeanDefinition 对象。
  • 如果 BeanDefinition 已经存在了,那么接下来会判断是否允许 BeanDefinition 覆盖,如果不允许,就直接抛出异常(不知道小伙伴们有没有印象,在松哥前面的 OAuth2 系列教程中,经常需要配置允许 BeanDefinition 的覆盖,就是因为这个原因,公众号【江南一点雨】后台回复 OAuth2 获取该教程),如果允许 BeanDefinition 的覆盖,那就向 beanDefinitionMap 中再次存一次值,覆盖之前的值。
  • 如果 BeanDefinition 不存在,那就直接注册。直接注册分两种情况:项目已经运行了和项目还没运行。
  • 如果项目已经运行,由于 beanDefinitionMap 是一个全局变量,可能存在并发问题,所以要加锁处理。否则就直接注册,所谓的注册就是把对象存入 beanDefinitionMap 中,同时将 beanName 都存入 beanDefinitionNames 集合中。

这便是 registerBeanDefinition 方法的工作流程。

有小伙伴会说,这个方法从头到尾都是 BeanDefinition,跟 Bean 有什么关系呢?

咋一看确实好像和 Bean 没有直接关系。

其实这涉及到另外一个问题,就是 Bean 的懒加载。这个时候先把 BeanDefinition 定义好,等到真正调用 Bean 的时候,才会去初始化 Bean。我们可以在 User 类的构造方法中打印日志看下,如下:

  1. public class User { 
  2.     private String username; 
  3.     private String address; 
  4.  
  5.     public User() { 
  6.         System.out.println("--------user init--------"); 
  7.     } 
  8.  
  9.     @Override 
  10.     public String toString() { 
  11.         return "User{" + 
  12.                 "username='" + username + '\'' + 
  13.                 ", address='" + address + '\'' + 
  14.                 '}'
  15.     } 
  16.  
  17.     public String getUsername() { 
  18.         return username; 
  19.     } 
  20.  
  21.     public void setUsername(String username) { 
  22.         this.username = username; 
  23.     } 
  24.  
  25.     public String getAddress() { 
  26.         return address; 
  27.     } 
  28.  
  29.     public void setAddress(String address) { 
  30.         this.address = address; 
  31.     } 

从下图可以看到,当 BeanDefinition 注册完成后,User 并没有初始化,等到 getBean 方法被调用的时候,User 才初始化了。

需要注意的是,我们日常开发中使用的 ApplicationContext 并非懒加载,这个在松哥的 Spring 入门视频中可以看到效果【??https://www.bilibili.com/video/BV1Wv41167TU】,具体原理松哥将在本系列后面的文章中和大家分享。

那么如果不想懒加载该怎么办呢?当然有办法。

4.提前注册 Bean

在 DefaultListableBeanFactory 中还有一个 preInstantiateSingletons 方法可以提前注册 Bean,该方法是在 ConfigurableListableBeanFactory 接口中声明的,DefaultListableBeanFactory 类实现了 ConfigurableListableBeanFactory 接口并实现了接口中的方法:

  1. @Override 
  2. public void preInstantiateSingletons() throws BeansException { 
  3.  if (logger.isTraceEnabled()) { 
  4.   logger.trace("Pre-instantiating singletons in " + this); 
  5.  } 
  6.  // Iterate over a copy to allow for init methods which in turn register new bean definitions. 
  7.  // While this may not be part of the regular factory bootstrap, it does otherwise work fine. 
  8.  List<String> beanNames = new ArrayList<>(this.beanDefinitionNames); 
  9.  // Trigger initialization of all non-lazy singleton beans... 
  10.  for (String beanName : beanNames) { 
  11.   RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName); 
  12.   if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) { 
  13.    if (isFactoryBean(beanName)) { 
  14.     Object bean = getBean(FACTORY_BEAN_PREFIX + beanName); 
  15.     if (bean instanceof FactoryBean) { 
  16.      final FactoryBean<?> factory = (FactoryBean<?>) bean; 
  17.      boolean isEagerInit; 
  18.      if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) { 
  19.       isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>) 
  20.           ((SmartFactoryBean<?>) factory)::isEagerInit, 
  21.         getAccessControlContext()); 
  22.      } 
  23.      else { 
  24.       isEagerInit = (factory instanceof SmartFactoryBean && 
  25.         ((SmartFactoryBean<?>) factory).isEagerInit()); 
  26.      } 
  27.      if (isEagerInit) { 
  28.       getBean(beanName); 
  29.      } 
  30.     } 
  31.    } 
  32.    else { 
  33.     getBean(beanName); 
  34.    } 
  35.   } 
  36.  } 
  37.  // Trigger post-initialization callback for all applicable beans... 
  38.  for (String beanName : beanNames) { 
  39.   Object singletonInstance = getSingleton(beanName); 
  40.   if (singletonInstance instanceof SmartInitializingSingleton) { 
  41.    final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance; 
  42.    if (System.getSecurityManager() != null) { 
  43.     AccessController.doPrivileged((PrivilegedAction<Object>) () -> { 
  44.      smartSingleton.afterSingletonsInstantiated(); 
  45.      return null
  46.     }, getAccessControlContext()); 
  47.    } 
  48.    else { 
  49.     smartSingleton.afterSingletonsInstantiated(); 
  50.    } 
  51.   } 
  52.  } 

preInstantiateSingletons 方法的整体逻辑比较简单,就是遍历 beanNames,对符合条件的 Bean 进行实例化,而且大家注意,这里所谓的提前初始化其实就是在我们调用 getBean 方法之前,它自己先调用了一下 getBean。

我们可以在案例中手动调用该方法:

  1. DefaultListableBeanFactory defaultListableBeanFactory = new DefaultListableBeanFactory(); 
  2. GenericBeanDefinition userBeanDefinition = new GenericBeanDefinition(); 
  3. MutablePropertyValues pvs = new MutablePropertyValues(); 
  4. pvs.add("username""javaboy"); 
  5. pvs.add("address""www.javaboy.org"); 
  6. userBeanDefinition.setPropertyValues(pvs); 
  7. userBeanDefinition.setBeanClass(User.class); 
  8. defaultListableBeanFactory.registerBeanDefinition("user", userBeanDefinition); 
  9. defaultListableBeanFactory.preInstantiateSingletons(); 
  10. User user = defaultListableBeanFactory.getBean(User.class); 
  11. System.out.println("user = " + user); 

此时在调用 getBean 方法之前,User 就已经初始化了,如下图:

5.getBean

DefaultListableBeanFactory 中另外一个重量级方法就是 getBean 了。不过 getBean 方法的真正实现是在 DefaultListableBeanFactory 的父类 AbstractBeanFactory 中,具体的实现方法是 doGetBean,本来想和大家子在这里聊一聊这个问题,但是发现这是一个非常庞大的问题,BeanFactory 和 FactoryBean 都还没和大家分享,所以这个话题我们还是暂且押后,一个点一个点来。

6.小结

好啦,今天就先说这么多,每篇源码我都尽量配置套一些小案例来演示,这样避免大家看的太枯燥了,我们下周继续~

本文转载自微信公众号「江南一点雨 」,可以通过以下二维码关注。转载本文请联系江南一点雨公众号。

【编辑推荐】

  1. 了解容器编排的构建块,可以使Kubernetes入门更加容易
  2. 聊聊 Python 应用容器化部署流程
  3. 容器编排之王,2020年Kubernetes有趣的12个数据
  4. 14个Java并发容器,你用过几个?
  5. Kubernetes容器网络模型
【责任编辑:武晓燕 TEL:(010)68476606】

点赞 0
分享:
大家都在看
猜你喜欢

订阅专栏+更多

大数据安全运维实战

大数据安全运维实战

CDH+Ambari
共20章 | 大数据陈浩

86人订阅学习

实操案例:Jenkins持续交付和持续部署

实操案例:Jenkins持续交付和持续部署

微服务架构下的自动化部署
共18章 | freshman411

174人订阅学习

思科交换网络安全指南

思科交换网络安全指南

安全才能无忧
共5章 | 思科小牛

106人订阅学习

订阅51CTO邮刊

点击这里查看样刊

订阅51CTO邮刊

51CTO服务号

51CTO官微