7.8 深入理解容器中的Bean 7.8.1 抽象Bean与子Bean

7.8 深入理解容器中的Bean

Spring框架绝大部分工作都集中在对容器中Bean的管理上,包括管理容器中Bean的生命周期、使用Bean继承等特殊功能。通过深入的管理,应用程序可以更好地使用这些Java组件(容器中的Bean对应用而言,往往是一个组件)。

7.8.1 抽象Bean与子Bean

在实际开发中,可能出现的场景是:随着项目越来越大, Spring配置文件中出现了多个<bean>配置具有大致相同的配置信息,只有少量信息不同,这将导致配置文件出现很多重复的内容。如果保留这种配置,则可能导致的问题是:

  • 配置文件臃肿。
  • 后期难以修改、维护。

为了解决上面问题,可以考虑把多个<bean>配置中相同的信息提取出来,集中成配置模板——这个配置模板并不是真正的Bean,因此Spring不应该创建该配置模板,于是需要**为该<bean>配置增加abstract="true"属性,这就是抽象Bean**。
抽象Bean不能被实例化, Spring容器不会创建抽象Bean实例。抽象Bean的价值在于被继承,抽象Bean通常作为父Bean让子Bean继承。
抽象Bean只是配置信息的模板,指定abstract="true"属性即可阻止Spring实例化该Bean,因此抽象Bean可以不指定class属性。

抽象Bean不能实例化

抽象Bean不能实例化,因此既不能通过getBean()显式地获得抽象Bean实例,也不能将抽象Bean注入成其他Bean依赖。不管怎样,只要程序企图实例化抽象Bean,都将导致错误.

父Bean中可以继承的配置信息

将大部分相同信息配置成抽象Bean之后,将实际的Bean实例配置成该抽象Bean的子Bean即可。子Bean定义可以从父Bean继承实现类构造器参数属性值配置信息,除此之外,子Bean配置可以增加新的配置信息,并可指定新的配置信息覆盖父Bean的定义。

子bean元素通过parent属性指定其父bean

通过为一个<bean>元素指定parent属性即可指定该Bean的父Bean, parent属性指定该Bean所继承的父Beanid.

父Bean中无法被继承的属性

Bean无法从父Bean继承如下属性:depends-onautowiresingletonscopelazy-init,这些属性将总是从子Bean定义中获得,或采用默认值
修改上面的配置文件如下,增加了子Bean定义。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0" encoding="GBK"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 定义Axe实例 -->
<bean id="steelAxe"
class="org.crazyit.app.service.impl.SteelAxe" />
<!-- 指定abstract="true"定义抽象Bean -->
<bean id="personTemplate" abstract="true">
<property name="name" value="crazyit" />
<property name="axe" ref="steelAxe" />
</bean>
<!-- 通过指定parent属性指定下面Bean配置可从父Bean继承得到配置信息 -->
<bean id="chinese" class="org.crazyit.app.service.impl.Chinese"
parent="personTemplate" />
<bean id="american"
class="org.crazyit.app.service.impl.American"
parent="personTemplate" />
</beans>

在配置文件中chineseamerican Bean都指定了parent="personTemplate"属性,这表明这两个Bean都可从父Bean那里继承得到配置信息,虽然这两个Bean都没有直接指定<property>子元素,但它们会从personTemplate模板那里继承得到两个<property>子元素。也就是说,上面的配置信息实际上相当于如下配置:

1
2
3
4
5
6
7
8
<bean id="chinese" class="org.crazyit.app.service.impl.Chinese">
<property name="name" value="crazyit" />
<property name="axe" ref="steelAxe" />
</bean>
<bean id="american" class="org.crazyit.app.service.impl.American">
<property name="name" value="crazyit" />
<property name="axe" ref="steelAxe" />
</bean>

使用抽象Bean的好处

不使用抽象Bean的配置方式不仅会导致配置文件臃肿,而且不利于项目后期的修改、维护,如果有一天项目需要改变chineseamericanname或所依赖的Axe对象,程序需要逐个修改每个Bean的配置信息。
如果使用了抽象Bean,则只需要修改Bean模板的配置即可,所有继承该Bean模板的子Bean的配置信息都会随之改变。

子类会覆盖父类继承来的相同的配置信息

如果父Bean(抽象Bean)指定了class属性,那么子Beanclass属性都可省略,子Bean将用与父Bean相同的实现类。除此之外,子Bean也可覆盖父Bean的配置信息:当子Bean拥有和父Bean相同的配置信息时,将使用以子Bean的配置信息。

项目结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
E:\workspace_QingLiangJiJavaEEQiYeYingYongShiZhang5\abstract
└─src\
├─beans.xml
├─lee\
│ └─BeanTest.java
└─org\
└─crazyit\
└─app\
└─service\
├─Axe.java
├─impl\
│ ├─American.java
│ ├─Chinese.java
│ ├─SteelAxe.java
│ └─StoneAxe.java
└─Person.java