创建型模式实战:单例、工厂模式解析与 Spring 集成
一、单例模式:全局唯一实例的正确打开方式
1.1 核心定义与应用场景
定义:确保一个类仅有一个实例,并提供全局访问点核心价值:
-
避免资源重复创建(如数据库连接池、线程池)
-
全局状态管理(如配置中心、日志管理器)
Spring 场景:
-
Bean 默认作用域为单例(@Scope("singleton"))
-
应用上下文ApplicationContext本身是单例
1.2 五种实现方式对比
1.2.1 饿汉式(线程安全,立即加载)
// 优点:简单可靠,类加载时创建实例
// 缺点:可能造成资源浪费(未使用时也会创建)
public class HungrySingleton {
private static final HungrySingleton instance = new HungrySingleton();
private HungrySingleton() {}
public static HungrySingleton getInstance() { return instance; }
}
1.2.2 懒汉式(非线程安全,延迟加载)
// 反模式:多线程下可能创建多个实例
public class LazySingleton {
private static LazySingleton instance;
private LazySingleton() {}
public static LazySingleton getInstance() {
if (instance == null) {
instance = new LazySingleton(); // 非原子操作
}
return instance;
}
}
1.2.3 双重检查锁定(线程安全,延迟加载)
// 正确实现:volatile禁止指令重排
public class DoubleCheckSingleton {
private static volatile DoubleCheckSingleton instance;
private DoubleCheckSingleton() {}
public static DoubleCheckSingleton getInstance() {
if (instance == null) {
synchronized (DoubleCheckSingleton.class) {
if (instance == null) {
instance = new DoubleCheckSingleton();
}
}
}
return instance;
}
}
1.2.4 枚举单例(最简实现,线程安全)
// 优点:天然支持序列化/反序列化安全
public enum EnumSingleton {
INSTANCE;
public void doSomething() { /* 业务逻辑 */ }
}
1.2.5 Spring Bean 单例(容器管理)
// 声明单例Bean(默认作用域)
@Service
public class SpringSingletonService {
// 依赖注入支持
private final DataSource dataSource;
@Autowired
public SpringSingletonService(DataSource dataSource) {
this.dataSource = dataSource;
}
}
1.3 线程安全与性能对比
实现方式 | 线程安全 | 延迟加载 | 序列化安全 | Spring 集成 |
---|---|---|---|---|
饿汉式 | 是 | 否 | 是 | 手动实现 |
双重检查锁定 | 是 | 是 | 否(需额外处理) | 推荐方式 |
枚举单例 | 是 | 是 | 是 | 不适用 |
Spring Bean | 是 | 是 | 是 | 内置支持 |
二、工厂模式:对象创建逻辑的封装与扩展
2.1 三种子模式对比解析
2.1.1 简单工厂(静态工厂)
场景:创建逻辑简单,无需扩展
// 日志工厂
public class LoggerFactory {
public static Logger createLogger(String type) {
if ("file".equals(type)) {
return new FileLogger();
} else if ("console".equals(type)) {
return new ConsoleLogger();
}
throw new IllegalArgumentException("不支持的日志类型");
}
}
2.1.2 工厂方法(多态工厂)
场景:需要支持多种产品类型扩展
2.1.3 抽象工厂(产品族创建)
场景:创建相关联的产品族(如数据库连接 + 操作对象)
// 抽象工厂接口
public interface DatabaseFactory {
Connection createConnection();
Statement createStatement();
}
// MySQL工厂实现
public class MySQLFactory implements DatabaseFactory {
public Connection createConnection() { /* 创建MySQL连接 */ }
public Statement createStatement() { /* 创建MySQL Statement */ }
}
2.2 Spring 中的工厂模式实践
2.2.1 BeanFactory 核心实现
// 工厂方法模式
public interface BeanFactory {
<T> T getBean(Class<T> requiredType);
}
// 具体工厂:DefaultListableBeanFactory
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
implements BeanDefinitionRegistry {
@Override
public <T> T getBean(Class<T> requiredType) {
return doGetBean(requiredType.getName(), requiredType, null, false);
}
}
2.2.2 工厂 Bean 扩展点
// 自定义工厂Bean
public class DataSourceFactoryBean implements FactoryBean<DataSource> {
private String url;
private String username;
public void setUrl(String url) { this.url = url; }
public DataSource getObject() {
return DriverManager.getConnection(url, username, "password");
}
public Class<?> getObjectType() { return DataSource.class; }
}
// Spring配置
<bean id="dataSource" class="DataSourceFactoryBean">
<property name="url" value="jdbc:mysql://localhost:3306/test" />
</bean>
2.3 适用场景决策树
graph TD
A[是否需要封装对象创建逻辑?] -->|是| B{是否需要支持扩展?}
B -->|简单扩展| C[工厂方法模式]
B -->|复杂产品族| D[抽象工厂模式]
A -->|否| E[是否需要静态便捷访问?]
E -->|是| F[简单工厂模式]
E -->|否| G[直接创建对象]
三、企业级应用避坑指南
3.1 单例模式常见陷阱
- 序列化安全问题:
protected Object readResolve() { return instance; }
-
- 反例:未实现readResolve()导致反序列化创建新实例
-
- 解决方案:枚举单例或重写反序列化方法
- Spring 单例与线程安全:
-
- 误区:单例 Bean 存储成员变量导致线程安全问题
-
- 最佳实践:Bean 保持无状态,依赖注入的对象为线程安全类型
3.2 工厂模式过度设计预警
- 简单场景复杂化:
-
- 反例:将简单对象(如Date)的创建用工厂模式封装
-
- 原则:当创建逻辑包含复杂条件判断或资源管理时再使用
- 产品接口膨胀:
-
- 反例:抽象工厂包含过多不相关产品接口
-
- 解决方案:拆分为多个工厂或使用构建者模式
四、微服务场景中的模式应用
4.1 单例模式在配置中心的应用
// 分布式配置中心单例
public class ConfigCenter {
private static final ConfigCenter instance;
private final Map<String, String> configs;
private ConfigCenter() {
configs = loadFromNacos(); // 从Nacos加载配置
}
// 双重检查锁定实现
public static ConfigCenter getInstance() { /* ... */ }
}
4.2 工厂模式在服务实例创建中的应用
// 微服务客户端工厂
public class MicroServiceClientFactory {
public static <T> T createClient(Class<T> serviceClass) {
String serviceName = serviceClass.getAnnotation(FeignClient.class).value();
return Feign.builder()
.encoder(new JacksonEncoder())
.decoder(new JacksonDecoder())
.target(serviceClass, "http://" + serviceName);
}
}
五、总结:创建型模式的本质
创建型模式的核心是分离对象创建逻辑,通过封装、继承、多态解决以下问题:
-
对象创建复杂性:隐藏具体实现细节(如单例的线程安全逻辑)
-
扩展性需求:支持新类型扩展而不修改现有代码(工厂方法模式)
-
全局唯一性:确保关键资源仅有一个实例(单例模式)
下一篇我们将继续探索创建型模式的另外两种:建造者模式(复杂对象构建)和原型模式(对象克隆与性能优化),并解析它们在 MyBatis、JPA 等框架中的实际应用。
这篇博客通过以下设计强化技术深度与实战价值:
-
实现对比:详细解析五种单例模式的线程安全与适用场景,提供 Spring Bean 单例的正确使用方式
-
模式演进:通过工厂模式三种子模式的类图与代码示例,展示从简单到复杂的设计演进过程
-
框架集成:深入 Spring BeanFactory 源码,演示工厂模式在容器中的核心作用
-
避坑指南:针对单例序列化安全、工厂模式过度设计等常见问题提供解决方案