spring-基础五
BeanDefinition 继承
Java 中继承是其面向对象的基石之一,继承就是之类继承父类的特征与行为,使得子类实例具有父类实例域或方法,或子类从父类继承方法,使得子类具有父类相同的行为.
Spring的继承又是怎样?
bean 定义可以包含很多的配置信息,包括构造函数的参数,属性值,容器的具体信息例如初始化方法,静态工厂方法名,等等。子 bean 的定义继承父定义的配置数据。子定义可以根据需要重写一些值,或者添加其他值。Spring Bean 定义的继承与 Java 类的继承无关,但是继承的概念是一样的。你可以定义一个父 bean 的定义作为模板和其他子 bean 就可以从父 bean 中继承所需的配置。当你使用基于 XML 的配置元数据时,通过使用父属性,指定父 bean 作为该属性的值来表明子 bean 的定义。
官网介绍Bean Definition 最后一句话很重要,可以理解为Spring 所说的继承其实是一种模版的继承,当我们创建实体的时候会使用模版,子类继承父类,其实是子类模版继承父模版,不是java 中单纯的子父类继承关系.子类模版拥有父类模版相关属性行为,并且可以进行覆盖或者重写.
按照官网的示例我们写个简单的demo
public static void main(String[] args) {
ClassPathXmlApplicationContext cc = new ClassPathXmlApplicationContext("application_bean.xml");
TestBeanB child = (TestBeanB) cc.getBean("child");
System.out.println("child name = " + child.getName());
System.out.println("child age = " + child.getAge());
}
//输出结果
//child name = override
//child age = 5
从上面结果中可以看到,子类name值没有被父类覆盖,但是age 覆盖了,也就是说,子类中已经存在的属性不会被父类所覆盖.
Spring的继承其实是一种合并过程,但是需要注意:
- 子BeanDefinition中的class属性如果为null,同时父BeanDefinition又指定了class属性,那么子BeanDefinition也会继承这个class属性。
- 子BeanDefinition必须要兼容父BeanDefinition中的所有属性。这是什么意思呢?以我们上面的demo为例,我们在父BeanDefinition中指定了name跟age属性,但是如果子BeanDefinition中子提供了一个name的setter方法,这个时候Spring在启动的时候会报错。因为子BeanDefinition不能承接所有来自父BeanDefinition的属性
BeanDefinition中abstract属性
在xml中并没有指定父类具体类,因此父类被定义成抽象,不能进行实例化,因为它是不完整的,并且它被显示的标记为abstract。当一个定义是abstract,它仅仅作为一个bean定义的模版且父bean定义为子bean定义服务。尝试自己使用这样的抽象父bean,通过将其引用为另一个bean的ref属性或使用父bean ID进行显式的getBean()调用将返回错误。类似地,容器的内部preInstantiateSingletons()方法将忽略定义为抽象的bean定义。
关于BeanDefinition中abstract属性的说明:
- 并不是作为父BeanDefinition就一定要设置abstract属性为true,abstract只代表了这个BeanDefinition是否要被Spring进行实例化并被创建对应的Bean,如果为true,代表容器不需要去对其进行实例化。
- 如果一个BeanDefinition被当作父BeanDefinition使用,并且没有指定其class属性。那么必须要设置其abstract为true
- abstract=true一般会跟父BeanDefinition一起使用,因为当我们设置某个BeanDefinition的abstract=true时,一般都是要将其当作BeanDefinition的模板使用,否则这个BeanDefinition也没有意义,除非我们使用其它BeanDefinition来继承它的属性
- 默认情况下,ApplicationContext会预先实例化所有单例。因此,重要的是(至少对于单例bean),如果有一个(父)bean定义仅打算用作模板,并且此定义指定了一个类,则必须确保将abstract属性设置为true ,否则应用程序上下文将实际上(试图)预先实例化抽象Bean。
BeanDefinition 方法说明
// 获取父BeanDefinition,主要用于合并
String getParentName();
// 设置的Bean的ClassName
void setBeanClassName(@Nullable String beanClassName);
// Bean的作用域,不考虑web容器,主要两种,单例/原型
void setScope(@Nullable String scope);
// 是否进行懒加载
void setLazyInit(boolean lazyInit);
// 是否需要等待指定的bean创建完之后再创建
void setDependsOn(@Nullable String... dependsOn);
// 是否作为自动注入的候选对象
void setAutowireCandidate(boolean autowireCandidate);
// 是否作为主选的Bean
void setPrimary(boolean primary);
// 创建这个bean的类的名称
void setFactoryBeanName(@Nullable String factoryBeanName);
// 创建这个Bean的方法的名称
void setFactoryMethodName(@Nullable String factoryMethodName);
// 构造函数的参数
ConstructorArgumentValues getConstructorArgumentValues();
// setter方法的参数
MutablePropertyValues getPropertyValues();
// 生命周期回调方法,在Bean完成属性注入后调用
void setInitMethodName(@Nullable String initMethodName);
// 生命周期回调方法,在Bean被销毁时调用
void setDestroyMethodName(@Nullable String destroyMethodName);
// Spring可以对BeanDefinition设置不同的角色,了解即可,不重要
// 用户定义 int ROLE_APPLICATION = 0;
// 某些复杂的配置 int ROLE_SUPPORT = 1;
// 完全内部使用 int ROLE_INFRASTRUCTURE = 2;
void setRole(int role);
// Bean的描述,没有什么实际含义
void setDescription(@Nullable String description);
// 根据scope判断是否是单例
boolean isSingleton();
// 根据scope判断是否是原型
boolean isPrototype();
// 跟合并BeanDefinition相关,如果是abstract,说明会被作为一个父beanDefinition,不用提供class属性
boolean isAbstract();
// Bean的源描述,没有什么实际含义
String getResourceDescription();
// cglib代理前的BeanDefinition
BeanDefinition getOriginatingBeanDefinition();