1. Spring框架对象实例化基础认知
在Spring框架中,对象实例化是IoC容器的核心功能之一。XML配置作为Spring传统的配置方式,虽然现在逐渐被注解取代,但在遗留系统维护和企业级应用中仍然广泛使用。通过XML配置实例化对象,本质上是通过配置文件告诉Spring容器:当需要某个对象时,应该用什么方式创建它。
我见过不少开发者虽然会用<bean>标签,但对底层实例化机制理解不深。比如有人会在配置prototype作用域的Bean时,奇怪为什么每次getBean()都返回新实例。其实这正体现了Spring实例化策略的灵活性——容器会根据配置决定何时以及如何创建对象。
2. 构造函数实例化(最常用方式)
2.1 基础配置与原理
这是Spring默认的实例化方式,通过在XML中声明<bean>标签的class属性实现:
xml复制<bean id="userService" class="com.example.UserServiceImpl"/>
当容器初始化时,会:
- 通过反射获取
UserServiceImpl的Class对象 - 调用默认无参构造函数(必须存在)
- 将生成的实例存入容器
注意:如果类没有无参构造,会抛出
BeanCreationException。这是新手常踩的坑,特别是在继承父类时忘记添加默认构造。
2.2 带参数构造的配置技巧
当需要注入构造参数时,使用<constructor-arg>标签:
xml复制<bean id="dataSource" class="com.example.BasicDataSource">
<constructor-arg value="jdbc:mysql://localhost:3306/test"/>
<constructor-arg value="root"/>
<constructor-arg value="123456"/>
</bean>
参数匹配规则:
- 按声明顺序匹配(如上例中的url,username,password)
- 可通过
index属性指定参数位置(从0开始) - 或使用
type属性指定参数类型(如type="int")
我推荐显式使用index或name属性(Spring 3.0+支持),避免因参数顺序调整导致意外:
xml复制<constructor-arg name="username" value="root"/>
3. 静态工厂方法实例化
3.1 适用场景分析
当对象的创建逻辑比较复杂,或者需要复用现有的工厂类时,可以使用静态工厂模式。典型场景包括:
- 连接池对象的创建
- 需要根据环境返回不同实现的场景
- 遗留系统中已经存在的工厂类
3.2 配置示例与陷阱规避
假设有个CalendarFactory类:
java复制public class CalendarFactory {
public static Calendar createCalendar(String type) {
if("chinese".equals(type)){
return ChineseCalendar.getInstance();
}
return GregorianCalendar.getInstance();
}
}
XML配置:
xml复制<bean id="calendar" class="com.example.CalendarFactory"
factory-method="createCalendar">
<constructor-arg value="chinese"/>
</bean>
常见问题:
- 忘记声明
factory-method会导致直接实例化工厂类 - 静态方法必须是public的
- 参数传递规则与构造函数相同
4. 实例工厂方法实例化
4.1 与静态工厂的区别
当工厂方法不是静态的时,需要先创建工厂实例,再调用其方法:
java复制public class ConnectionFactory {
public Connection createConnection() throws SQLException {
return DriverManager.getConnection(...);
}
}
XML配置需要两步:
xml复制<!-- 1. 先声明工厂bean -->
<bean id="connectionFactory" class="com.example.ConnectionFactory"/>
<!-- 2. 声明产品bean并指定工厂 -->
<bean id="connection" factory-bean="connectionFactory"
factory-method="createConnection"/>
4.2 生命周期管理要点
这种情况下,工厂bean的生命周期会影响产品bean:
- 如果工厂是singleton,每次获取的是新连接但使用同一工厂
- 如果工厂是prototype,每次都会创建新工厂和新连接
对于数据库连接这种资源,通常需要配合destroy-method:
xml复制<bean id="connection" factory-bean="connectionFactory"
factory-method="createConnection"
destroy-method="close"/>
5. FactoryBean接口实现方式
5.1 高级定制化实例方案
Spring提供了FactoryBean接口,适合需要复杂初始化逻辑的场景:
java复制public class MyFactoryBean implements FactoryBean<ComplexObject> {
@Override
public ComplexObject getObject() {
// 复杂的构建逻辑
return new ComplexObject(/*...*/);
}
@Override
public Class<?> getObjectType() {
return ComplexObject.class;
}
@Override
public boolean isSingleton() {
return true;
}
}
XML配置看似普通:
xml复制<bean id="complexObj" class="com.example.MyFactoryBean"/>
但实际获取的是ComplexObject而非工厂本身。要获取工厂实例需要在id前加&:
java复制FactoryBean<?> factory = ctx.getBean("&complexObj");
5.2 典型应用场景
- 整合第三方库(如Hibernate的SessionFactory)
- 需要大量配置的对象(如Spring的JndiObjectFactoryBean)
- AOP代理对象的创建
6. 实例化策略对比与选型建议
| 方式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 构造函数 | 简单直接,Spring原生支持 | 无法处理复杂初始化逻辑 | 大多数常规场景 |
| 静态工厂 | 复用现有工厂代码 | 灵活性有限 | 整合遗留系统 |
| 实例工厂 | 工厂本身可被DI管理 | 配置稍复杂 | 需要运行时决定的实例创建 |
| FactoryBean | 完全控制实例化过程 | 实现成本较高 | 框架集成等高级场景 |
根据我的经验:
- 80%的场景用构造函数方式就够了
- 遇到需要条件判断的创建逻辑,优先考虑FactoryBean
- 整合老系统时可能会用到静态/实例工厂
7. XML配置的现代演进
虽然现在推荐使用Java Config,但了解XML配置仍有价值:
- 维护老项目必备技能
- 某些场景(如Spring Batch的job定义)仍以XML为主
- 理解底层机制有助于调试注解配置的问题
从Spring 4.x开始,XML配置也可以结合SpEL表达式实现更灵活的实例化:
xml复制<bean id="envBean" class="#{systemProperties['env'] == 'prod' ?
'com.example.ProdImpl' : 'com.example.DevImpl'}"/>
8. 实战中的坑与解决方案
问题1:循环依赖导致实例化失败
- 现象:A依赖B,B又依赖A
- 方案:使用setter注入替代构造注入,或调整设计
问题2:初始化异常处理
xml复制<bean id="failSafeBean" class="com.example.UnstableBean"
init-method="init" destroy-method="cleanup">
<property name="retryCount" value="3"/>
</bean>
问题3:性能优化
- 对于prototype bean,考虑使用
@Scope+方法注入 - 大量bean初始化时,使用
depends-on控制顺序
我处理过一个线上案例:系统启动时加载200+个bean导致超时。最终通过lazy-init="true"分批加载解决了问题:
xml复制<bean id="heavyBean" class="..." lazy-init="true"/>