BeanDefinition 继承图
对于 BeanDefinition 有一个抽象类的实现是 AbstractBeanDefinition,而 AbstractBeanDefinition 有很多子类的实现,如:GenericBeanDefinition,ChildBeanDefinition,RootBeanDefinition 等
AbstractBeanDefinition 是一个抽象的 BD,所以不能被实例化,所以我们要用 BD 来描述一个 Bean,就需要用到 AbstractBeanDefinition 的实现类
那么问题来 ,如果我们希望手工注册一个 BeanDefinition 的话,我们选择哪一个呢?
选择什么样的 BeanDefinition ?
其实通过名字就可以看出来,我们可以选择 GenericBeanDefinition(通用的 BD),为什么呢?
ChildBeanDefinition
我们先看 Spring 对 ChildBeanDefinition 的说明
* NOTE: Since Spring 2.5, the preferred way to register bean
* definitions programmatically is the {@link GenericBeanDefinition} class,
* which allows to dynamically define parent dependencies through the
* {@link GenericBeanDefinition#setParentName} method. This effectively
* 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 会抛出异常**
@Override
public void setParentName(@Nullable String parentName) {
if (parentName != null) {
throw new IllegalArgumentException("Root bean cannot be changed into a child bean with parent reference");
}
}
当作 BD 的模板进行使用
当 RBD 作为 BD 模板使用的时候,就不要设置 BeanClass 属性了,需要使用 rbd.setAbstract(true);
来标识其为一个 Abstract Class,以供其他类使用
RootBeanDefinition rbd = new RootBeanDefinition();
rbd.setScope(BeanDefinition.SCOPE_SINGLETON);
rbd.setLazyInit(false);
rbd.setAbstract(true);
当作为一个真实的 BD
RBD 也可以当作一个真实的 BD 来使用,需要设置 rbd.setBeanClass(xxx.class)
,就不需要设置 Abstract 属性了
public class App {
public static void main(String[] args) {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
ac.register(AppConfig.class);
RootBeanDefinition rbd = new RootBeanDefinition();
rbd.setScope(BeanDefinition.SCOPE_SINGLETON);
rbd.setLazyInit(false);
rbd.setBeanClass(UserService.class);
ac.registerBeanDefinition("userService", rbd);
// 相当于继承了 RootBeanDefinition
ChildBeanDefinition cbd = new ChildBeanDefinition("userService");
cbd.setBeanClass(IndexService.class);
ac.registerBeanDefinition("indexService", cbd);
ac.refresh();
System.out.println(ac.getBean("userService"));
System.out.println(ac.getBean("indexService"));
}
}
GenericBeanDefinition
GenericBeanDefinition 是 Spring 2.5 之后推出的,既可以做普通 BD,也可以做子 BD,甚至可以做父 BD,可以完全替代 ChildBeanDefinition(通过 setParentName),但是不能完全取代 RootBeanDefinition
既然 GBD 可以做父 BD,那么为什么不能完全取代 RootBeanDefinition 呢? 这和 bean 的合并有关系,后面的章节会详细解释
public class App {
public static void main(String[] args) {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
ac.register(AppConfig.class);
GenericBeanDefinition rbd = new GenericBeanDefinition();
rbd.setLazyInit(false);
rbd.setScope(GenericBeanDefinition.SCOPE_SINGLETON);
rbd.setBeanClass(UserService.class);
ac.registerBeanDefinition("userService", rbd);
GenericBeanDefinition cbd = new GenericBeanDefinition();
cbd.setParentName("userService");
cbd.setBeanClass(IndexService.class);
ac.registerBeanDefinition("indexService", cbd);
ac.refresh();
System.out.println(ac.getBean("userService"));
System.out.println(ac.getBean("indexService"));
}
}
ConfigurationClassBeanDefinition
这个 BD 又是做什么的呢?我们知道在 Spring 中,我们经常会使用 @Bean 的注解来注入一些第三方类,此时这些类就会用 ConfigurationClassBeanDefinition 来定义
ScannedGenericBeanDefinition
见名知意,一定和扫描有关,对咯,这个 BD,就是 Spring 在进行扫描的时候,发现有 @Component 或者 @Service 等注解标注的 bean 的时候,就会用 ScannedGenericBeanDefinition 来定义
private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
// ... 省略代码无数
if (isCandidateComponent(metadataReader)) {
// ★ 创建了 ScannedGenericBeanDefinition
// ★ 这里就证明了通过注解扫描出来的类都是 ScannedGenericBeanDefinition
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
sbd.setResource(resource);
sbd.setSource(resource);
// 关键代码:返回是(否是一个独立的类(可以通过构造函数创建)并且 不能是接口和抽象类) 或者(是一个抽象类,并且 有 Lookup 注解)
if (isCandidateComponent(sbd)) {
if (debugEnabled) {
logger.debug("Identified candidate component class: " + resource);
}
candidates.add(sbd);
}
else {
if (debugEnabled) {
logger.debug("Ignored because not a concrete top-level class: " + resource);
}
}
}
// 省略代码无数
}
AnnotatedGenericBeanDefinition
这个 DB 描述的,通常都是 Spring 初始化时,通过 register 来注册的 bean,如:加了 @Configuration 的注解类,就是用 AnnotatedGenericBeanDefinition 来定义的
@Override
public void register(Class<?>... componentClasses) {
Assert.notEmpty(componentClasses, "At least one component class must be specified");
this.reader.register(componentClasses);
}
这里的 reader 其实就是 private final AnnotatedBeanDefinitionReader reader;