BeanDefinition 继承图

AbstractBeanDefinition.png
对于 BeanDefinition 有一个抽象类的实现是 AbstractBeanDefinition,而 AbstractBeanDefinition 有很多子类的实现,如:GenericBeanDefinition,ChildBeanDefinition,RootBeanDefinition 等

AbstractBeanDefinition 是一个抽象的 BD,所以不能被实例化,所以我们要用 BD 来描述一个 Bean,就需要用到 AbstractBeanDefinition 的实现类

那么问题来 ,如果我们希望手工注册一个 BeanDefinition 的话,我们选择哪一个呢?

选择什么样的 BeanDefinition ?

其实通过名字就可以看出来,我们可以选择 GenericBeanDefinition(通用的 BD),为什么呢?

ChildBeanDefinition

我们先看 Spring 对 ChildBeanDefinition 的说明

  1. * NOTE: Since Spring 2.5, the preferred way to register bean
  2. * definitions programmatically is the {@link GenericBeanDefinition} class,
  3. * which allows to dynamically define parent dependencies through the
  4. * {@link GenericBeanDefinition#setParentName} method. This effectively
  5. * supersedes the ChildBeanDefinition class for most use cases.

必须设置 parent bean 定义,spring 并没有提供无参构造方法,必须通过构造函数指定,只能作为子 BD 一般使用 ChildBeanDefinition 是确定了父子关系的 BeanDefinition

在 spring2.5 之前据说是配合 RootBeanDefinition 一起使用 但是现在的版本可以配合 {@link GenericBeanDefinition#setParentName} 使用了 未来很可能会被标记过时或者淘汰

这个类的实例化一般是通过构造方法设置的,并且一定要传 父BD 的名字 这是 ChildBeanDefinition 最大的局限了。 所以 ChildBeanDefinition 并不能当作一个普通的 DB 存在

所以这个 Child BD,我们现在已经不在使用了

RootBeanDefinition

RootBeanDefinition 可以配合 ChildBeanDefinition 一起使用,这样相同的代码我们就可以不需要重复编写,这就是 RootBeanDefinition 的作用

RootBeanDefinition 通常作为 parent BD 出现的,也可以作为普通 BD,唯一**不能作为 子 BD 出现,因为作为子类出现的话,setParentName 会抛出异常**

  1. @Override
  2. public void setParentName(@Nullable String parentName) {
  3. if (parentName != null) {
  4. throw new IllegalArgumentException("Root bean cannot be changed into a child bean with parent reference");
  5. }
  6. }

当作 BD 的模板进行使用

当 RBD 作为 BD 模板使用的时候,就不要设置 BeanClass 属性了,需要使用 rbd.setAbstract(true); 来标识其为一个 Abstract Class,以供其他类使用

  1. RootBeanDefinition rbd = new RootBeanDefinition();
  2. rbd.setScope(BeanDefinition.SCOPE_SINGLETON);
  3. rbd.setLazyInit(false);
  4. rbd.setAbstract(true);

当作为一个真实的 BD

RBD 也可以当作一个真实的 BD 来使用,需要设置 rbd.setBeanClass(xxx.class) ,就不需要设置 Abstract 属性了

  1. public class App {
  2. public static void main(String[] args) {
  3. AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
  4. ac.register(AppConfig.class);
  5. RootBeanDefinition rbd = new RootBeanDefinition();
  6. rbd.setScope(BeanDefinition.SCOPE_SINGLETON);
  7. rbd.setLazyInit(false);
  8. rbd.setBeanClass(UserService.class);
  9. ac.registerBeanDefinition("userService", rbd);
  10. // 相当于继承了 RootBeanDefinition
  11. ChildBeanDefinition cbd = new ChildBeanDefinition("userService");
  12. cbd.setBeanClass(IndexService.class);
  13. ac.registerBeanDefinition("indexService", cbd);
  14. ac.refresh();
  15. System.out.println(ac.getBean("userService"));
  16. System.out.println(ac.getBean("indexService"));
  17. }
  18. }

GenericBeanDefinition

GenericBeanDefinition 是 Spring 2.5 之后推出的,既可以做普通 BD,也可以做子 BD,甚至可以做父 BD,可以完全替代 ChildBeanDefinition(通过 setParentName),但是不能完全取代 RootBeanDefinition

既然 GBD 可以做父 BD,那么为什么不能完全取代 RootBeanDefinition 呢? 这和 bean 的合并有关系,后面的章节会详细解释

  1. public class App {
  2. public static void main(String[] args) {
  3. AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
  4. ac.register(AppConfig.class);
  5. GenericBeanDefinition rbd = new GenericBeanDefinition();
  6. rbd.setLazyInit(false);
  7. rbd.setScope(GenericBeanDefinition.SCOPE_SINGLETON);
  8. rbd.setBeanClass(UserService.class);
  9. ac.registerBeanDefinition("userService", rbd);
  10. GenericBeanDefinition cbd = new GenericBeanDefinition();
  11. cbd.setParentName("userService");
  12. cbd.setBeanClass(IndexService.class);
  13. ac.registerBeanDefinition("indexService", cbd);
  14. ac.refresh();
  15. System.out.println(ac.getBean("userService"));
  16. System.out.println(ac.getBean("indexService"));
  17. }
  18. }

ConfigurationClassBeanDefinition

这个 BD 又是做什么的呢?我们知道在 Spring 中,我们经常会使用 @Bean 的注解来注入一些第三方类,此时这些类就会用 ConfigurationClassBeanDefinition 来定义

ScannedGenericBeanDefinition

见名知意,一定和扫描有关,对咯,这个 BD,就是 Spring 在进行扫描的时候,发现有 @Component 或者 @Service 等注解标注的 bean 的时候,就会用 ScannedGenericBeanDefinition 来定义

参考@ComponentScan 详解

  1. private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
  2. // ... 省略代码无数
  3. if (isCandidateComponent(metadataReader)) {
  4. // ★ 创建了 ScannedGenericBeanDefinition
  5. // ★ 这里就证明了通过注解扫描出来的类都是 ScannedGenericBeanDefinition
  6. ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
  7. sbd.setResource(resource);
  8. sbd.setSource(resource);
  9. // 关键代码:返回是(否是一个独立的类(可以通过构造函数创建)并且 不能是接口和抽象类) 或者(是一个抽象类,并且 有 Lookup 注解)
  10. if (isCandidateComponent(sbd)) {
  11. if (debugEnabled) {
  12. logger.debug("Identified candidate component class: " + resource);
  13. }
  14. candidates.add(sbd);
  15. }
  16. else {
  17. if (debugEnabled) {
  18. logger.debug("Ignored because not a concrete top-level class: " + resource);
  19. }
  20. }
  21. }
  22. // 省略代码无数
  23. }

AnnotatedGenericBeanDefinition

这个 DB 描述的,通常都是 Spring 初始化时,通过 register 来注册的 bean,如:加了 @Configuration 的注解类,就是用 AnnotatedGenericBeanDefinition 来定义的

  1. @Override
  2. public void register(Class<?>... componentClasses) {
  3. Assert.notEmpty(componentClasses, "At least one component class must be specified");
  4. this.reader.register(componentClasses);
  5. }

这里的 reader 其实就是 private final AnnotatedBeanDefinitionReader reader;

Spring实例化过程.jpg