bean的继承和依赖
Spring Bean 的继承与依赖:复用配置与控制实例化顺序
在 Spring 中,Bean 的继承和依赖是两种重要的配置机制。继承用于复用配置(减少重复代码),依赖用于控制 Bean 的实例化顺序。详细解析这两种机制的实现方式、使用场景及注意事项。
Bean 的继承:复用配置,减少冗余
Spring 中的 Bean 继承并非 Java 中的类继承(不涉及类的父子关系),而是配置的继承:子 Bean 可以继承父 Bean 的所有配置(属性、依赖等),并可覆盖父 Bean 的配置。这类似于 “模板模式”,父 Bean 定义通用配置,子 Bean 仅需定义差异化部分。
1. 基本使用:通过 parent 属性实现继承
(1)普通 Bean 作为父 Bean
父 Bean 可以是一个可实例化的普通 Bean,子 Bean 通过 parent 属性指定父 Bean 的 id,从而继承其配置。
示例:
<!-- 父 Bean:定义通用配置 -->
<bean id="parentPerson" class="com.zhanghe.study.spring4.beans.beantest.Person">
<property name="name" value="张三"/> <!-- 通用属性:姓名 -->
<property name="car" ref="car"/> <!-- 通用依赖:Car 对象 -->
<property name="cars"> <!-- 通用集合:List<Car> -->
<list>
<ref bean="car"/>
<ref bean="car2"/>
</list>
</property>
</bean>
<!-- 子 Bean:继承父 Bean 并覆盖部分配置 -->
<bean id="sonPerson" parent="parentPerson">
<!-- 覆盖父 Bean 的 name 属性 -->
<property name="name" value="张飞"/>
<!-- 继承父 Bean 的 car、cars 配置,无需重复定义 -->
</bean>
效果:sonPerson 会继承 parentPerson 的 car、cars 配置,仅 name 被覆盖为 “张飞”。
(2)抽象 Bean:专门用于被继承的 “模板”
如果一个 Bean 仅作为父 Bean 被继承,无需实例化(也不能实例化),可将其定义为抽象 Bean(abstract="true")。
特点:
- 抽象 Bean 不能被实例化(调用
getBean("抽象BeanId")会报错); - 抽象 Bean 的
class属性可选(若子 Bean 会指定class,父 Bean 可省略class); - 抽象 Bean 仅用于配置复用,本身不生成实例。
示例:
<!-- 抽象 Bean:仅作为模板,不实例化 -->
<bean id="abstractPerson" abstract="true">
<!-- 定义通用配置(无需 class,子 Bean 会指定) -->
<property name="car" ref="car"/>
<property name="cars">
<list>
<ref bean="car"/>
<ref bean="car2"/>
</list>
</property>
</bean>
<!-- 子 Bean:继承抽象 Bean,需指定自身 class -->
<bean id="personSon" class="com.zhanghe.study.spring4.beans.beantest.Person" parent="abstractPerson">
<property name="name" value="张飞"/> <!-- 仅定义差异化属性 -->
</bean>
错误场景:若尝试实例化抽象 Bean,会抛出 BeanIsAbstractException:
// 错误:尝试获取抽象 Bean
Person abstractPerson = context.getBean("abstractPerson", Person.class);
// 抛出异常:org.springframework.beans.factory.BeanIsAbstractException: Error creating bean with name 'abstractPerson': Bean definition is abstract
2. 继承的规则与限制
- 配置覆盖:子 Bean 可覆盖父 Bean 的任何配置(属性、依赖、初始化方法等);
class继承:若父 Bean 指定了class,子 Bean 可省略class(继承父类类型);若子 Bean 指定class,需确保与父 Bean 类型兼容(如子类或同一类型);- 抽象性继承:子 Bean 不会继承父 Bean 的
abstract属性(需手动指定abstract="true"才会成为抽象 Bean); - 依赖继承:父 Bean 的
depends-on配置会被子 Bean 继承(子 Bean 可追加新依赖)。
3. 适用场景
- 通用配置复用:多个 Bean 共享大量相同配置(如数据库连接池的默认参数、服务类的通用依赖);
- 模板化配置:定义 “基础模板”,不同子 Bean 仅修改少量属性(如不同环境的配置差异)。
Bean 的依赖:控制实例化顺序
Spring 容器默认按配置顺序实例化 Bean,但有时需要确保某个 Bean 先于目标 Bean 实例化(如目标 Bean 依赖该 Bean 的初始化数据)。此时可通过 depends-on 属性强制指定依赖关系。
1. 基本使用:depends-on 强制依赖顺序
depends-on 用于声明:目标 Bean 依赖于指定 Bean,容器必须先实例化依赖的 Bean,再实例化目标 Bean。
示例:
<!-- 被依赖的 Bean:必须先实例化 -->
<bean id="leader" class="com.zhanghe.study.spring4.beans.beantest.Person">
<property name="name" value="刘备"/>
</bean>
<!-- 目标 Bean:依赖 leader,需在 leader 之后实例化 -->
<bean id="follower" class="com.zhanghe.study.spring4.beans.beantest.Person" depends-on="leader">
<property name="name" value="关羽"/>
</bean>
效果:容器会先实例化 leader,再实例化 follower。
2. 多依赖处理
depends-on 支持指定多个依赖 Bean(用逗号、空格或分号分隔),容器会按顺序实例化所有依赖 Bean,再实例化目标 Bean。
示例:
<!-- 多个被依赖的 Bean -->
<bean id="beanA" class="com.example.BeanA"/>
<bean id="beanB" class="com.example.BeanB"/>
<!-- 依赖多个 Bean:先实例化 beanA、beanB,再实例化 targetBean -->
<bean id="targetBean" class="com.example.TargetBean" depends-on="beanA, beanB"/>
3. 错误场景:依赖的 Bean 不存在
若 depends-on 指定的 Bean 不存在,容器初始化时会抛出 NoSuchBeanDefinitionException:
<!-- 错误配置:依赖不存在的 beanX -->
<bean id="testBean" class="com.example.TestBean" depends-on="beanX"/>
异常信息:
org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'testBean' defined in class path resource [spring-config.xml]:
'testBean' depends on missing bean 'beanX'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException:
No bean named 'beanX' available
4. 与自动装配的区别
depends-on:仅控制实例化顺序,不负责注入依赖(需手动通过ref或@Autowired注入);- 自动装配(
@Autowired/ref):负责注入依赖,且隐含依赖顺序(被注入的 Bean 会先实例化)。
示例:若 follower 通过 ref 注入 leader,则无需 depends-on(注入本身已保证顺序):
<bean id="leader" class="com.zhanghe.study.spring4.beans.beantest.Person"/>
<bean id="follower" class="com.zhanghe.study.spring4.beans.beantest.Person">
<property name="leader" ref="leader"/> <!-- 注入隐含依赖顺序,无需 depends-on -->
</bean>
5. 适用场景
- 非直接依赖的初始化顺序:当 Bean A 不直接依赖 Bean B,但 A 的初始化需要 B 已初始化(如 B 初始化时加载了 A 所需的全局配置);
- 资源释放顺序:
depends-on还会影响销毁顺序(目标 Bean 先销毁,依赖的 Bean 后销毁),适合资源释放有依赖的场景(如先销毁连接池使用者,再销毁连接池)。
总结:继承与依赖的最佳实践
- Bean 继承:
- 用于配置复用,减少重复代码;
- 抽象 Bean 作为 “模板”,仅被继承,不实例化;
- 子 Bean 可覆盖父 Bean 的配置,保持灵活性。
- Bean 依赖:
- 用于控制实例化 / 销毁顺序,解决非直接依赖的初始化问题;
- 优先通过注入(
ref/@Autowired)隐含依赖顺序,仅在必要时使用depends-on; - 避免循环依赖(如 A 依赖 B,B 依赖 A),会导致容器初始化失败