什么是 BeanDefinition?

我们都知道 Spring 再启动时,首先要扫描所有符合要求的 bean,然后对 bean 进行解析,再将扫描的 bean 保存起来(默认单例模式下),然后再进行校验,后而走 bean 的生命周期(实例化),如下所示的一个 bean

  1. @Component
  2. @DependsOn("userService")
  3. @Scope("singleton")
  4. public class IndexService {
  5. public IndexService(UserService userService){
  6. System.out.println(userService);
  7. }
  8. }

我们可以看到,在这个 bean 上有很多注解,而且 Spring 并不是扫描后就进行实例化 bean,那么这么多 bean 的信息是如何保存的呢?

Spring 提供了一个对象 BeanDefinition 来保存这些信息,以便后面进行实例化,简单的理解,就是 Spring 中的 bean 的 class 对象(对 bean 的抽象),后期用来创建 bean

BeanDefinition 定义

BeanDefinition 是一个接口,并定义了很多常量和方法,它有一个实现类 AbstractBeanDefinition(抽象类)
image.png

  1. public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
  2. String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;
  3. String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;
  4. int ROLE_APPLICATION = 0;
  5. int ROLE_SUPPORT = 1;
  6. int ROLE_INFRASTRUCTURE = 2;
  7. /** 设置 父DB 的名称 */
  8. void setParentName(@Nullable String parentName);
  9. @Nullable
  10. String getParentName();
  11. /** 设置 Bean 的类型 -- 字符串 */
  12. void setBeanClassName(@Nullable String beanClassName);
  13. @Nullable
  14. String getBeanClassName();
  15. /** 设置 作用域 */
  16. void setScope(@Nullable String scope);
  17. @Nullable
  18. String getScope();
  19. /** 设置 懒加载 */
  20. void setLazyInit(boolean lazyInit);
  21. boolean isLazyInit();
  22. /** 设置依赖 */
  23. void setDependsOn(@Nullable String... dependsOn);
  24. @Nullable
  25. String[] getDependsOn();
  26. /**
  27. * 设置 是否作为自动装配的候选
  28. */
  29. void setAutowireCandidate(boolean autowireCandidate);
  30. boolean isAutowireCandidate();
  31. /** 是否是主要装配对象 */
  32. void setPrimary(boolean primary);
  33. boolean isPrimary();
  34. /** 设置 工厂Bean -- 字符串 */
  35. void setFactoryBeanName(@Nullable String factoryBeanName);
  36. @Nullable
  37. String getFactoryBeanName();
  38. /** 设置 工厂Bean的方法 */
  39. void setFactoryMethodName(@Nullable String factoryMethodName);
  40. @Nullable
  41. String getFactoryMethodName();
  42. /** 存储构造方法的参数 */
  43. ConstructorArgumentValues getConstructorArgumentValues();
  44. default boolean hasConstructorArgumentValues() {
  45. return !getConstructorArgumentValues().isEmpty();
  46. }
  47. MutablePropertyValues getPropertyValues();
  48. default boolean hasPropertyValues() {
  49. return !getPropertyValues().isEmpty();
  50. }
  51. /** 设置生命周期初始化方法的名字 */
  52. void setInitMethodName(@Nullable String initMethodName);
  53. @Nullable
  54. String getInitMethodName();
  55. /** 设置生命周期销毁方法的名字 */
  56. void setDestroyMethodName(@Nullable String destroyMethodName);
  57. @Nullable
  58. String getDestroyMethodName();
  59. void setRole(int role);
  60. int getRole();
  61. /** 设置 bean 的描述 */
  62. void setDescription(@Nullable String description);
  63. @Nullable
  64. String getDescription();
  65. /** 是否 单例 */
  66. boolean isSingleton();
  67. /** 是否 原型 */
  68. boolean isPrototype();
  69. /** 是否 抽象 */
  70. boolean isAbstract();
  71. @Nullable
  72. String getResourceDescription();
  73. @Nullable
  74. BeanDefinition getOriginatingBeanDefinition();
  75. }

细心的小伙伴可以发现,BeanDefinition 中的属性在 XML 中都可以找到,对拉,其实 Spring 最初引入这个 BeanDefinition 就是为了可以通过代码来实现 Bean 的注入,但是后期由于 XML 和注解的盛行,这种方式慢慢被淘汰

无论是单例还是原型 bean 都会放在 beanDefinitionMap 中

那么 AttributeAccessor, BeanMetadataElement 又是做什么的呢?
**
BeanMetadataAttributeAccessor extends AttributeAccessorSupport implements BeanMetadataElement

BeanMetadataAttributeAccessor 是对 AttributeAccessor 和 BeanMetadataElement 的统一实现
**

AttributeAccessor

AttributeAccessor 其实就是一个 Map,存放当前类的一些配置信息

  1. Object attribute = ac.getBeanDefinition("myService").getAttribute("org.springframework.context.annotation.ConfigurationClassPostProcessor.configurationClass");
  2. System.out.println(attribute); // lite

BeanMetadataElement

BeanMetadataElement 提供了一个方法 getSource 返回了当前 bean 所存放的物理路径

  1. Object source = ac.getBeanDefinition("myService").getSource();
  2. System.out.println(source); // file [D:\Documents\Java\Spring\spring\spring-framework-5.1.x\research-spring\out\production\classes\org\wesoft\spring\inject\services\MyService.class]
  1. AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
  2. ac.register(AppConfig.class);
  3. ac.refresh();
  4. GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
  5. beanDefinition.setBeanClass(TestBean.class);
  6. beanDefinition.setScope(GenericBeanDefinition.SCOPE_SINGLETON);
  7. beanDefinition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_TYPE);
  8. ac.registerBeanDefinition("testBean", beanDefinition);
  9. System.out.println(ac.getBean(TestBean.class));

getBean 获取 Bean

对于下面这段代码,大家比较熟悉,基本上都会翻译成从Spring 容器中获取 bean

  1. AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
  2. ac.register(AppConfig.class);
  3. ac.refresh();
  4. ac.getBean(TestBean.class)

我们来看一下 getBean 的源码,通过跟踪 getBean 的源码,最终会调用下面一段代码

  1. Object singletonObject = this.singletonObjects.get(beanName);

也就是从 singletonObjects 通过 beanName 获取 bean,那么 singletonObjects 又是什么呢?

  1. private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

我们可以看到 singletonObjects 其实就是一个 Map 对象,我们通常叫做单例池,那么也就是说 getBean 最终是从一个 map 中获取 bean 的对象,我们可以简单的理一下

ac.getBean = 从容器中获取 bean = singletonObjects.get = map.get

singletonObjects(单例池)

singletonObjects 又称为单例池,实现是一个 ConcurrentHashMap,这个单例池存放了所有 Scope 等于 singleton 的 bean 对象,Scope 为 prototype 的 bean 对象并不会在此存储,当我们每次 getBean 的时候,就是从 singletonObjects 中获取 bean 对象,如果是 prototype 对象的话,则每次获取都会重新创建一个新的

beanDefinitionMap(Bean的定义)

beanDefinitionMap 本身也是一个 ConcurrentHashMap,beanDefinitionMap 主要存放 bean 的定义,所以所有的 Spring bean 的定义都会在这里存储,方便后面对 bean 进行校验,如:当解析 prototype bean 时,由于其 beanDefinition 中的 isPrototype 返回 true,所以就不会将 prototype bean 放入单例池

现在我们知道了,是先将 bean 的定义存放到 beanDefinitionMap 中,然后对 beanDefinitionMap 中的 bean 进行校验,校验通过的 bean(非懒加载的单例类)放入 singletonObjects 中,当我们执行 getBean 的时候,就会直接从 singletonObjects 中获取

那么问题来了,如果我们在将 bean 放入 singletonObjects 之前,对 该 bean 的 beanDefinition 做一些手脚,会发生什么情况呢?

BeanFactoryPostProcessor(Bean 工厂后置处理器)

BeanFactoryPostProcessor 是一个接口,执行时机是当 beanDefinition 放入到 beanDefinitionMap 之后会回调这个接口中的 postProcessBeanFactory 方法

这正好符合我们的需求,我们可以尝试修改 beanDefinitionMap 中的 beanDefinition,让它指向其他类

  1. @Component
  2. public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
  3. /*
  4. 在 put 完 beanDefinitionMap 之后,调用 postProcessBeanFactory
  5. */
  6. @Override
  7. public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
  8. GenericBeanDefinition beanDefinition = (GenericBeanDefinition) beanFactory.getBeanDefinition("beanService");
  9. beanDefinition.setBeanClass(TestBean.class);
  10. }
  11. }
  12. public static void main(String[] args) {
  13. AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
  14. ac.register(AppConfig.class);
  15. ac.refresh();
  16. System.out.println(ac.getBean("beanService"));
  17. System.out.println(ac.getBean(TestBean.class));
  18. }
  19. // org.wesoft.spring.inject.bean.TestBean@65fb9ffc
  20. // org.wesoft.spring.inject.bean.TestBean@65fb9ffc

我们可以看到,此时 ac.getBean(“beanService”) 返回的已经不是 BeanService 了,而是 TestBean,同样,我们通过 ac.getBean(TestBean.class) 也可以获取到 TestBean