一、什么是内部 bean?
内部 bean(inner bean)是指定义在另一个 bean 内部的 bean。它不需要单独的 id,也不能被外部容器直接引用,只服务于其所在的宿主 bean。
<!-- 外部 bean -->
<bean id="userservice" class="com.example.userservice">
<property name="userdao">
<!-- 内部 bean:没有 id,只属于 userservice -->
<bean class="com.example.userdaoimpl">
<property name="datasource" ref="datasource"/>
</bean>
</property>
</bean>userdaoimpl 就是一个内部 bean。它没有 id,外部无法通过 getbean() 获取它。它的生命周期完全绑定在 userservice 上。
二、xml 中的内部 bean
2.1 基本用法
内部 bean 通过 <bean> 标签嵌套在 <property> 或 <constructor-arg> 中定义。
<bean id="orderservice" class="com.example.orderservice">
<property name="paymentservice">
<bean class="com.example.alipayservice">
<property name="appid" value="2021001"/>
<property name="privatekey" value="xxxxx"/>
</bean>
</property>
</bean>特点:
- 不需要
id属性,即使写了也会被忽略 - 不能通过
ref引用 - 不能被其他 bean 引用
- 每次注入时都会创建新实例(即使宿主 bean 是单例)
2.2 构造器参数中的内部 bean
<bean id="userservice" class="com.example.userservice">
<constructor-arg>
<bean class="com.example.userdaoimpl"/>
</constructor-arg>
</bean>2.3 内部 bean 的匿名性
内部 bean 本质上是一个匿名 bean。spring 会为它生成一个内部名称,但开发者无法预知也无法引用。它只能被它的宿主 bean 使用。
三、java 配置中的内部 bean
在 java 配置中,内部 bean 通常通过 @bean 方法实现,或者直接在方法体内 new 出对象。
3.1 直接 new 对象
@configuration
public class appconfig {
@bean
public userservice userservice() {
// 内部 bean:直接 new,不注册到容器
userdao userdao = new userdaoimpl();
return new userservice(userdao);
}
}
这种方式创建的对象不是 spring bean,不受容器管理,不享受依赖注入和 aop 增强。
3.2 通过 @bean 方法调用(推荐)
@configuration
public class appconfig {
@bean
public userservice userservice() {
// 调用 userdao() 方法,由于 cglib 代理,返回的是容器中的单例
return new userservice(userdao());
}
@bean
public userdao userdao() {
return new userdaoimpl();
}
}
@configuration 类会被 cglib 增强,userdao() 方法调用返回的是容器中的 bean,而不是新创建的实例。
3.3 真正意义上的内部 bean
java 配置中没有严格意义上的“内部 bean”。如果希望对象完全私有,不被容器管理,可以这样写:
@configuration
public class appconfig {
@bean
public userservice userservice() {
userdaoimpl userdao = new userdaoimpl();
userdao.setdatasource(datasource()); // 手动注入依赖
return new userservice(userdao);
}
@bean
public datasource datasource() {
return new hikaridatasource();
}
}
userdao 是一个纯 java 对象,不是 spring bean,但它的依赖 datasource 来自容器。
四、内部 bean 与外部 bean 的对比
| 对比维度 | 内部 bean | 外部 bean |
|---|---|---|
| 定义位置 | 嵌套在宿主 bean 内部 | 独立定义 |
| 是否有 id | 没有(匿名) | 有唯一 id |
| 是否可被引用 | 不能被其他 bean 引用 | 可以被任意引用 |
| 生命周期 | 随宿主 bean 创建/销毁 | 独立管理 |
| 作用域 | 与宿主 bean 绑定 | 可配置多种作用域 |
| 适用场景 | 仅宿主 bean 使用的私有依赖 | 多个 bean 共享的公共依赖 |
五、内部 bean 的生命周期
内部 bean 的生命周期与宿主 bean 紧密绑定:
- 创建:宿主 bean 被创建时,内部 bean 同时被创建
- 销毁:宿主 bean 被销毁时,内部 bean 同时被销毁
- 作用域:内部 bean 不支持自定义作用域,始终与宿主 bean 同生命周期
- aop 代理:内部 bean 可以被 aop 代理,但代理对象无法被外部引用
六、内部 bean 的典型使用场景
6.1 私有依赖
当某个依赖只被一个 bean 使用时,用内部 bean 可以避免污染容器。
<bean id="reportservice" class="com.example.reportservice">
<property name="datasource">
<bean class="com.example.reportdatasource">
<property name="url" value="jdbc:mysql://report-db:3306/report"/>
</bean>
</property>
</bean>reportdatasource 只被 reportservice 使用,不需要暴露给容器。
6.2 多实例配置
同一个类需要不同的配置实例时,用内部 bean 可以避免创建多个有 id 的 bean。
<bean id="emailservice" class="com.example.emailservice">
<property name="mailsender">
<bean class="com.example.smtpmailsender">
<property name="host" value="smtp.company.com"/>
</bean>
</property>
</bean>
<bean id="alertservice" class="com.example.alertservice">
<property name="mailsender">
<bean class="com.example.smtpmailsender">
<property name="host" value="smtp.alert.com"/>
</bean>
</property>
</bean>两个 smtpmailsender 实例使用不同的 smtp 服务器,互不干扰。
6.3 一次性对象
某些对象只在某个 bean 初始化时使用一次,不需要长期存在于容器中。
七、内部 bean 的注意事项
1. 内部 bean 不能使用 @autowired
内部 bean 如果是通过 xml 定义的匿名 bean,无法被 @autowired 注入到其他类中,因为它没有可引用的 id。
2. 内部 bean 的 id 被忽略
<bean id="userservice" class="com.example.userservice">
<property name="userdao">
<bean id="innerdao" class="com.example.userdaoimpl"/>
<!-- 这个 id 会被忽略,无法通过 getbean("innerdao") 获取 -->
</property>
</bean>3. 内部 bean 的循环引用
内部 bean 无法引用外部 bean,因为它没有 id。但外部 bean 可以引用内部 bean(通过嵌套定义)。
4. 每次注入创建新实例
如果宿主 bean 是单例,内部 bean 也是单例(与宿主绑定)。如果宿主 bean 是 prototype,每次创建宿主时都会创建新的内部 bean 实例。
5. java 配置中 new 出来的对象不是 bean
@bean
public userservice userservice() {
userdao userdao = new userdaoimpl(); // 这不是 spring bean
return new userservice(userdao);
}
这个 userdao 不受容器管理,依赖注入、aop、生命周期回调都不生效。
八、最佳实践
优先使用外部 bean。 除非确实只需要宿主 bean 使用,否则尽量定义为外部 bean。外部 bean 更灵活,可以被测试直接获取,也可以被其他 bean 复用。
合理使用 java 配置。 在 java 配置中,通过 @bean 方法调用获取依赖,而不是直接 new。这样依赖仍然是 spring bean,享受容器管理。
避免过度嵌套。 内部 bean 嵌套层级过深会导致 xml 难以阅读和维护。如果嵌套超过两层,考虑提取为外部 bean。
明确内部 bean 的定位。 内部 bean 适合“私有、一次性、不共享”的依赖。如果需要复用或测试,应该定义为外部 bean。
九、总结
| 维度 | 核心要点 |
|---|---|
| 定义 | 嵌套在宿主 bean 内部,没有独立 id |
| 生命周期 | 与宿主 bean 绑定,同生共死 |
| 引用 | 不能被外部 bean 引用,只能被宿主使用 |
| xml 写法 | <bean> 嵌套在 <property> 或 <constructor-arg> 中 |
| java 写法 | 通过 @bean 方法调用或直接 new |
| 适用场景 | 私有依赖、多实例配置、一次性对象 |
| 注意事项 | 无法被 @autowired、id 被忽略、每次创建新实例 |
内部 bean 是 spring 提供的一种“私有 bean”机制,适合那些不需要暴露给容器、只服务于特定宿主 bean 的依赖。合理使用内部 bean,可以让容器中的 bean 定义更清晰,避免不必要的 bean 污染。在 java 配置为主流的今天,内部 bean 的使用频率已经降低,但在一些需要为同一个类创建多个配置实例的场景下,它仍然是一个有用的工具。
以上就是spring注入内部bean的方法步骤的详细内容,更多关于spring注入内部bean的资料请关注代码网其它相关文章!
发表评论