1. 非自定义Bean实例化方式解析
在Spring框架中,Bean的实例化方式主要分为三种:构造器实例化、静态工厂方法实例化和实例工厂方法实例化。本文将重点探讨如何通过配置非自定义Bean(如JDK内置类或第三方库类)来演示这些实例化方式。
1.1 工厂实例化方式(非静态)
1.1.1 原理剖析
非静态工厂方法实例化是指通过一个已存在的Bean实例(工厂Bean)来创建另一个Bean实例。这种方式特别适用于那些需要通过特定对象方法才能创建的实例。
以java.util.Date和java.text.SimpleDateFormat为例:
- SimpleDateFormat作为工厂Bean
- 通过其parse()方法创建Date对象
这种模式实现了:
- 对象创建逻辑的封装
- 复杂初始化过程的隔离
- 实例创建过程的标准化
注意:工厂Bean必须先于目标Bean定义,因为Spring容器会按照定义顺序初始化Bean
1.1.2 具体实现步骤
XML配置示例:
xml复制<!-- 1. 首先定义工厂Bean -->
<bean class="java.text.SimpleDateFormat" id="simpleDateFormat">
<constructor-arg name="pattern" value="yyyy-MM-dd HH:mm:ss"/>
</bean>
<!-- 2. 使用工厂Bean创建目标Bean -->
<bean class="java.util.Date" id="date"
factory-bean="simpleDateFormat"
factory-method="parse">
<constructor-arg name="source" value="2020-05-05 09:09:09"/>
</bean>
关键参数说明:
factory-bean:指定工厂Bean的IDfactory-method:指定创建实例的方法名constructor-arg:传递给工厂方法的参数
测试代码:
java复制public class BeanFactoryTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext(
"applicationContext_beanfac.xml");
// 获取通过工厂方法创建的Date实例
Date date = (Date) context.getBean("date");
System.out.println(date);
}
}
1.1.3 常见问题与解决方案
问题1:工厂Bean未正确初始化
- 现象:抛出NoSuchBeanDefinitionException
- 原因:工厂Bean定义在目标Bean之后
- 解决:确保工厂Bean的定义顺序在前
问题2:参数类型不匹配
- 现象:抛出BeanCreationException
- 原因:构造函数参数类型与工厂方法参数类型不兼容
- 解决:检查参数类型并确保一致
问题3:时间格式无效
- 现象:抛出ParseException
- 原因:日期字符串与pattern格式不匹配
- 解决:确保日期字符串严格遵循pattern格式
1.2 静态工厂方法实例化
1.2.1 原理与实现
静态工厂方法实例化通过类的静态方法来创建Bean实例,不需要先创建工厂Bean。
以MyBatis的Resources类为例:
java复制public static InputStream getResourceAsStream(String resource)
XML配置示例:
xml复制<bean class="org.apache.ibatis.io.Resources"
factory-method="getResourceAsStream"
id="in">
<constructor-arg name="resource" value="mybatis-config.xml"/>
</bean>
特点:
- 不需要factory-bean属性
- 直接指定包含静态方法的类
- 适用于工具类创建实例的场景
2. 综合案例:MyBatis SqlSessionFactory实例化
2.1 准备工作
MyBatis基础配置:
xml复制<!-- mybatis-config.xml -->
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="org/mybatis/example/BlogMapper.xml"/>
</mappers>
</configuration>
2.2 完整实例化流程
2.2.1 配置方案
xml复制<!-- 1. 静态工厂方法:获取配置输入流 -->
<bean class="org.apache.ibatis.io.Resources"
factory-method="getResourceAsStream"
id="in">
<constructor-arg name="resource" value="mybatis-config.xml"/>
</bean>
<!-- 2. 构造器实例化:SqlSessionFactoryBuilder -->
<bean class="org.apache.ibatis.session.SqlSessionFactoryBuilder"
id="builder"/>
<!-- 3. 实例工厂方法:创建SqlSessionFactory -->
<bean id="sqlSessionFactory"
class="org.apache.ibatis.session.SqlSessionFactory"
factory-bean="builder"
factory-method="build">
<constructor-arg name="inputStream" ref="in"/>
</bean>
2.2.2 测试代码
java复制public class MyBatisTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext(
"applicationContext.xml");
// 获取SqlSessionFactory实例
SqlSessionFactory factory = (SqlSessionFactory)
context.getBean("sqlSessionFactory");
// 创建SqlSession
SqlSession session = factory.openSession();
System.out.println("SqlSession created: " + session);
session.close();
}
}
2.3 技术细节分析
生命周期管理:
- Resources静态方法创建的InputStream由Spring管理生命周期
- SqlSessionFactoryBuilder是短暂存在的工具对象
- SqlSessionFactory通常是单例的
- SqlSession需要手动关闭
性能优化建议:
- 将SqlSessionFactory配置为单例
- 使用连接池配置数据源
- 考虑使用Spring-MyBatis整合包简化配置
3. 实例化方式对比与选型
3.1 三种实例化方式对比
| 特性 | 构造器实例化 | 静态工厂方法 | 实例工厂方法 |
|---|---|---|---|
| 配置复杂度 | 简单 | 中等 | 较复杂 |
| 灵活性 | 低 | 中 | 高 |
| 适用场景 | 简单对象创建 | 工具类对象创建 | 复杂对象创建 |
| 依赖管理 | 直接依赖 | 间接依赖 | 间接依赖 |
| 性能影响 | 无 | 轻微 | 轻微 |
3.2 选型建议
- 优先使用构造器实例化:当类有简单构造器且不需要复杂初始化逻辑时
- 考虑静态工厂方法:当目标类提供静态创建方法时(如各种Utils类)
- 使用实例工厂方法:当对象创建需要其他实例配合时(如解析器、转换器等)
经验分享:在实际项目中,80%的场景可以使用构造器实例化,15%使用静态工厂方法,只有5%需要实例工厂方法。不要过度设计,选择最简单的可行方案。
4. 高级应用与最佳实践
4.1 复杂对象初始化
对于需要多步初始化的对象,可以采用组合方式:
xml复制<!-- 分步初始化示例 -->
<bean id="complexObjectBuilder" class="com.example.ComplexObjectBuilder"/>
<bean id="step1" factory-bean="complexObjectBuilder" factory-method="buildStep1">
<constructor-arg value="param1"/>
</bean>
<bean id="step2" factory-bean="complexObjectBuilder" factory-method="buildStep2">
<constructor-arg ref="step1"/>
<constructor-arg value="param2"/>
</bean>
<bean id="finalObject" factory-bean="complexObjectBuilder" factory-method="finalizeBuild">
<constructor-arg ref="step2"/>
</bean>
4.2 初始化回调
Spring提供了多种初始化回调机制:
- InitializingBean接口
- @PostConstruct注解
- init-method配置
推荐实践:
xml复制<bean id="service" class="com.example.Service"
init-method="init" destroy-method="cleanup">
<property name="config" ref="config"/>
</bean>
4.3 性能优化技巧
-
延迟初始化:对于不立即需要的Bean
xml复制<bean id="lazyBean" class="com.example.ExpensiveBean" lazy-init="true"/> -
作用域控制:合理使用prototype作用域
xml复制<bean id="processor" class="com.example.Processor" scope="prototype"/> -
依赖检查:确保必要依赖已配置
xml复制<bean id="client" class="com.example.Client" dependency-check="objects"/>
5. 常见问题深度解析
5.1 循环依赖问题
典型场景:
java复制class A {
@Autowired B b;
}
class B {
@Autowired A a;
}
解决方案:
- 使用setter注入替代构造器注入
- 调整Bean初始化顺序
- 使用@Lazy延迟初始化
5.2 实例化异常处理
常见异常类型:
- BeanCreationException:实例化失败
- NoSuchBeanDefinitionException:Bean未定义
- UnsatisfiedDependencyException:依赖不满足
调试技巧:
- 检查Bean定义是否正确
- 验证依赖关系
- 查看完整堆栈信息
- 使用getBean()手动测试
5.3 环境相关配置
Profile使用示例:
xml复制<beans profile="dev">
<bean id="dataSource" class="...">
<!-- 开发环境配置 -->
</bean>
</beans>
<beans profile="prod">
<bean id="dataSource" class="...">
<!-- 生产环境配置 -->
</bean>
</beans>
激活Profile:
java复制// 编程式激活
System.setProperty("spring.profiles.active", "dev");
// 或通过JVM参数
// -Dspring.profiles.active=dev
在实际项目开发中,合理选择Bean实例化方式可以显著提高代码的可维护性和灵活性。建议根据具体场景选择最合适的方案,并遵循Spring的最佳实践。对于复杂的对象创建逻辑,可以考虑使用FactoryBean接口实现更精细的控制。