0%

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,从而继承其配置。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!-- 父 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 会继承 parentPersoncarcars 配置,仅 name 被覆盖为 “张飞”。

(2)抽象 Bean:专门用于被继承的 “模板”

如果一个 Bean 仅作为父 Bean 被继承,无需实例化(也不能实例化),可将其定义为抽象 Beanabstract="true")。

特点

  • 抽象 Bean 不能被实例化(调用 getBean("抽象BeanId") 会报错);
  • 抽象 Bean 的 class 属性可选(若子 Bean 会指定 class,父 Bean 可省略 class);
  • 抽象 Bean 仅用于配置复用,本身不生成实例。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!-- 抽象 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

1
2
3
// 错误:尝试获取抽象 Bean
Person abstractPerson = context.getBean("abstractPerson", Person.class);
// 抛出异常:org.springframework.beans.factory.BeanIsAbstractException: Error creating bean with name 'abstractPerson': Bean definition is abstract

2. 继承的规则与限制

  1. 配置覆盖:子 Bean 可覆盖父 Bean 的任何配置(属性、依赖、初始化方法等);
  2. class 继承:若父 Bean 指定了 class,子 Bean 可省略 class(继承父类类型);若子 Bean 指定 class,需确保与父 Bean 类型兼容(如子类或同一类型);
  3. 抽象性继承:子 Bean 不会继承父 Bean 的 abstract 属性(需手动指定 abstract="true" 才会成为抽象 Bean);
  4. 依赖继承:父 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

示例:

1
2
3
4
5
6
7
8
9
<!-- 被依赖的 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。

示例:

1
2
3
4
5
6
<!-- 多个被依赖的 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

1
2
<!-- 错误配置:依赖不存在的 beanX -->
<bean id="testBean" class="com.example.TestBean" depends-on="beanX"/>

异常信息:

1
2
3
4
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(注入本身已保证顺序):

1
2
3
4
5
<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 后销毁),适合资源释放有依赖的场景(如先销毁连接池使用者,再销毁连接池)。

总结:继承与依赖的最佳实践

  1. Bean 继承
    • 用于配置复用,减少重复代码;
    • 抽象 Bean 作为 “模板”,仅被继承,不实例化;
    • 子 Bean 可覆盖父 Bean 的配置,保持灵活性。
  2. Bean 依赖
    • 用于控制实例化 / 销毁顺序,解决非直接依赖的初始化问题;
    • 优先通过注入(ref/@Autowired)隐含依赖顺序,仅在必要时使用 depends-on
    • 避免循环依赖(如 A 依赖 B,B 依赖 A),会导致容器初始化失败

欢迎关注我的其它发布渠道