1. 单例模式
单例模式是一种创建型设计模式 ,单例模式确保一个类只有一个实例,并提供全局访问点。例如,在应用程序中创建数据库连接池、缓存服务、日志管理器等,这些只需要单一实例的组件常采用单例模式实现,以避免资源浪费和状态混乱。
写法:
- 双重检查锁+volatile懒汉式
Java
public class Singleton {
// 使用 volatile 关键字确保当 uniqueInstance 被初始化成 Singleton 实例时,
// 多个线程可以正确处理 uniqueInstance 对象的可见性
private volatile static Singleton uniqueInstance;
// 构造函数私有化,防止外部直接实例化
private Singleton() {}
// 提供一个全局可访问的静态方法,返回唯一的 Singleton 实例
public static Singleton getInstance() {
if (uniqueInstance == null) { // 第一次检查:如果实例未创建
synchronized (Singleton.class) { // 加锁
if (uniqueInstance == null) { // 第二次检查:在同步块内再次检查
uniqueInstance = new Singleton(); // 创建唯一实例
}
}
}
return uniqueInstance;
}
}
- 静态内部类懒汉式
Java
public class Singleton {
// 私有构造函数,防止外部直接实例化
private Singleton() {}
// 定义一个静态内部类,该类仅在首次访问时被加载,且只会加载一次
private static class SingletonHolder {
// 静态内部类中创建Singleton的唯一实例
private static final Singleton INSTANCE = new Singleton();
}
// 提供一个全局可访问的静态方法,返回唯一的Singleton实例
public static Singleton getInstance() {
return SingletonHolder.INSTANCE; // 直接返回静态内部类中已创建的实例
}
}
例子: Spring Bean的单例模式。对于单例Bean,Spring会确保只创建一次实例,并将其缓存起来。这通常通过一个内部Map(如DefaultSingletonBeanRegistry
的singletonObjects
)来实现。对于后续的相同Bean请求,Spring容器直接从缓存中获取已创建的单例实例返回,无需再次创建
2. 工厂模式
工厂模式是一种创建型设计模式 ,工厂模式提供了一种创建对象的封装机制,隐藏对象的具体创建过程,使用者只需关注所需产品的接口。例如,在数据库连接场景中,我们可以创建一个ConnectionFactory,根据配置信息返回MySQLConnection或OracleConnection,无需用户关心底层连接细节。
例子:
Spring框架的源码中广泛采用了工厂模式,无论是基础的BeanFactory
接口,还是更具体的实现类如DefaultListableBeanFactory
,都是工厂模式在Spring框架中的具体体现。通过提供getBean方法,让用户不用关心具体细节就能得到Bean对象,这极大地简化了对象的创建和管理,实现了依赖的解耦和配置的集中化。
3. 工厂方法模式
工厂方法模式是一种创建型设计模式 ,是工厂模式的一种变体,抽象工厂(接口或抽象类)提供创建某一类对象的方法,对应的实际创建逻辑推迟到具体工厂(实现类)中。 例如,不同类型的图形(如圆形、矩形)可以由各自的图形工厂类生成,客户端仅调用抽象工厂的创建方法,由子类决定具体创建哪种图形对象。
例子:
BeanFactory和ApplicationContext接口两个接口就像工厂方法模式中的抽象工厂,提供了创建Bean的通用接口,它们的实现类(如DefaultListableBeanFactory
、XmlBeanFactory
、AnnotationConfigApplicationContext
等)相当于具体工厂,在创建Bean时遵循了工厂方法模式,根据配置信息(如XML配置文件、注解配置等)来决定创建哪些Bean以及如何创建。
4. 抽象工厂模式
抽象工厂模式是一种创建型设计模式 ,是工厂方法模式的一种变体,与工厂方法模式思想类似,工厂方法模式关注于创建单一产品族的不同产品,抽象工厂模式关注于创建多个产品族的整套产品。例如,操作系统主题包中,一个抽象工厂可以提供创建窗口、按钮、菜单等一系列风格一致的UI组件,客户端通过选择不同的具体工厂(如Windows主题工厂、Mac主题工厂)来获得整套风格的组件。
例子:
JDBC。JDBC(Java Database Connectivity)规范定义了DriverManager
类,它负责加载并管理各种数据库驱动。当需要连接数据库时,开发人员调用DriverManager.getConnection()
方法,传入数据库URL和凭据。DriverManager
会根据URL识别所需的驱动类型,通过驱动工厂(即实现了java.sql.Driver
接口的类)创建与特定数据库系统的连接对象。此外,通过连接对象可以获取到Statement
、PreparedStatement
、ResultSet
等对象,这些对象也是由驱动工厂提供的,形成了一个与特定数据库系统相关的对象集合。
5. 建造者模式
建造者模式是一种创建型设计模式 。建造者模式将复杂对象的构建与表示分离,使得相同的构建过程可以创建不同的表示。例如,构建复杂的订单对象时,可以使用Builder类逐步添加商品、设置收货地址、选择支付方式等,最后得到完整订单。
例子:
- 在Spring的IoC容器中,
BeanDefinitionBuilder
类被用来构建BeanDefinition
对象 ,这是描述Bean配置信息的一个复杂数据结构。通过BeanDefinitionBuilder
,开发者可以逐步设置Bean的各种属性、构造函数参数、依赖项等,最终得到完整的BeanDefinition
,用于注册到Spring容器中。这种构建过程遵循建造者模式,隐藏了BeanDefinition内部的复杂性,提供了清晰、易用的API。
typescript
public final class BeanDefinitionBuilder {
/**
* Create a new {@code BeanDefinitionBuilder} used to construct a {@link GenericBeanDefinition}.
*/
public static BeanDefinitionBuilder genericBeanDefinition() {
return new BeanDefinitionBuilder(new GenericBeanDefinition());
}
/**
* Create a new {@code BeanDefinitionBuilder} used to construct a {@link GenericBeanDefinition}.
* @param beanClassName the class name for the bean that the definition is being created for
*/
public static BeanDefinitionBuilder genericBeanDefinition(String beanClassName) {
BeanDefinitionBuilder builder = new BeanDefinitionBuilder(new GenericBeanDefinition());
builder.beanDefinition.setBeanClassName(beanClassName);
return builder;
}
......
......
......
}
- Configuration对象的构建 : MyBatis的核心配置对象
Configuration
包含了数据库连接信息、映射器、类型处理器、插件等各种复杂的配置细节。由于配置信息的多样性与复杂性,直接通过一个构造函数来初始化Configuration
对象是不切实际的。因此,MyBatis使用了XMLConfigBuilder
(解析XML配置文件)和SqlSessionFactoryBuilder
(进一步处理配置信息并创建SqlSessionFactory
)等类作为构建器,它们按照一定的步骤逐步构建并填充Configuration
对象,实现了建造者模式。
Java
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(reader);
6. 适配器模式
适配器模式是一种行为型设计模式。适配器模式将一个类的接口转换为客户期望的另一个接口,使得原本不兼容的类可以一起工作。例如,将第三方库的API封装为项目中统一的接口,或者在不同数据源之间进行数据格式转换时,都可以应用适配器模式。
例子:
- Advice适配 : Spring AOP提供了若干个内置的Advice适配器类,如
MethodBeforeAdviceAdapter
、AfterReturningAdviceAdapter
、ThrowsAdviceAdapter
等,它们分别实现了对不同类型的Advice(如前置通知、后置通知、异常通知等)的适配,使得这些Advice能够被Spring AOP代理机制正确识别和应用。
Java
public DefaultAdvisorAdapterRegistry() {
registerAdvisorAdapter(new MethodBeforeAdviceAdapter());
registerAdvisorAdapter(new AfterReturningAdviceAdapter());
registerAdvisorAdapter(new ThrowsAdviceAdapter());
}
Java
class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {
@Override
public boolean supportsAdvice(Advice advice) {
return (advice instanceof MethodBeforeAdvice);
}
@Override
public MethodInterceptor getInterceptor(Advisor advisor) {
MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
return new MethodBeforeAdviceInterceptor(advice);
}
}
Java
class AfterReturningAdviceAdapter implements AdvisorAdapter, Serializable {
@Override
public boolean supportsAdvice(Advice advice) {
return (advice instanceof AfterReturningAdvice);
}
@Override
public MethodInterceptor getInterceptor(Advisor advisor) {
AfterReturningAdvice advice = (AfterReturningAdvice) advisor.getAdvice();
return new AfterReturningAdviceInterceptor(advice);
}
}
Java
class ThrowsAdviceAdapter implements AdvisorAdapter, Serializable {
@Override
public boolean supportsAdvice(Advice advice) {
return (advice instanceof ThrowsAdvice);
}
@Override
public MethodInterceptor getInterceptor(Advisor advisor) {
return new ThrowsAdviceInterceptor(advisor.getAdvice());
}
}
- MVC(模型-视图-控制器) : Spring MVC中的
HandlerAdapter
接口及其实现类构成了适配器模式的一个典型应用场景。HandlerAdapter
的作用是将各种符合特定约定的处理器(如Controller
、HttpRequestHandler
等)适配到Spring MVC的请求处理流程中。这样,用户可以编写符合自己习惯的控制器类,而Spring MVC通过选择合适的HandlerAdapter
来调用这些控制器的方法,处理HTTP请求并返回响应,无需控制器直接与MVC框架的内部细节耦合。 - 日志模块 : MyBatis的日志系统是适配器模式的一个经典示例。MyBatis并未直接实现自己的日志功能,而是提供了一个抽象的
Log
接口,然后针对市面上流行的日志框架(如SLF4J、Log4j、Log4j2、JUL、JCL等)编写了对应的适配器类,如Slf4jImpl
、Log4jImpl
等,这些类实现了Log
接口并将日志操作委托给相应的日志框架。这样,无论用户项目中使用何种日志库,MyBatis都能通过适配器找到合适的日志实现进行日志记录,保持了与外部日志系统的松耦合。
7. 装饰器模式
装饰器模式是一种行为型设计模式 。装饰器模式即不改变原始组件的职责,而是通过动态地给组件添加"装饰"以增强其功能。在不改变原有类的情况下,通过组合而非继承的方式为对象扩展功能。如在咖啡订购系统中,通过添加各种调料(如糖、奶、巧克力酱)装饰基础咖啡,实现多种口味咖啡的组合。
例子:
-
装饰器模式在MyBatis的缓存机制中有典型应用 。MyBatis允许用户配置启用或禁用缓存,并且支持不同的缓存实现,例如一级缓存和二级缓存。当启用缓存时,MyBatis会使用装饰器模式来动态地增强
Executor
对象的能力,具体做法是在执行器之上添加一层缓存装饰器,如CachingExecutor
。原始的Executor
负责处理SQL执行的基本逻辑,而CachingExecutor
作为装饰器在其基础上增加了缓存读写的功能 。这样做的好处在于,无论何时需要切换缓存策略或者禁用缓存,都不需要修改Executor
的原有代码,而是通过添加或移除装饰器来完成。 -
HTTP请求的过滤器链(如Servlet规范中的Filter)、拦截器链(如Spring MVC中的Interceptor)就是装饰器模式在实际应用中的经典示例。
-
在Servlet规范中,Filter接口允许开发者编写自定义的过滤器,它们可以在HTTP请求到达Servlet之前以及离开Servlet之后执行某些操作,比如身份验证、日志记录、字符编码转换等。多个Filter可以串联起来形成一个过滤器链,每一个Filter都在原始请求处理流程的基础上添加了一层额外的功能,这正是装饰器模式的体现。
-
在Spring MVC中,Interceptor(拦截器)也可以在控制器方法执行前后添加额外的操作,如统一异常处理、性能监控、权限验证等。尽管拦截器在技术实现上可能借助了AOP(面向切面编程)的思想,但从设计模式的角度看,它仍然符合装饰器模式的特点
-
8. 代理模式
代理模式是一种行为型设计模式。代理模式为对象提供一个代理对象,以便控制对原对象的访问。例如,远程代理用于网络通信中为本地对象提供远程对象的访问接口;保护代理可以控制对敏感对象的访问权限;缓存代理可以缓存目标对象的昂贵计算结果。
例子:
-
Spring AOP(面向切面编程) : Spring AOP利用代理模式来实现诸如事务管理、日志记录、性能监控等功能。Spring通过创建代理对象(JDK动态代理或CGLIB代理)来包裹目标对象,当客户端调用代理对象的方法时,会触发代理方法,代理方法会在目标方法执行前后插入相应的通知(Advice),从而实现切面逻辑的织入。
- JDK动态代理 :如果目标类实现了至少一个接口,Spring会使用JDK的
java.lang.reflect.Proxy
类来创建一个代理对象,代理对象的方法调用会被转发至InvocationHandler实现类处理,该实现类内嵌了切面逻辑。 - CGLIB代理:对于没有实现接口的目标类,Spring会选择CGLIB库来生成一个继承自目标类的子类代理,子类重写了父类方法并在方法内部加入切面逻辑。
- JDK动态代理 :如果目标类实现了至少一个接口,Spring会使用JDK的
-
Spring容器对Bean的生命周期管理 : Spring IoC容器在管理Bean的生命周期时,也会使用代理来增强Bean的功能,比如通过后置处理器(PostProcessor)对Bean进行进一步的设置和初始化。
9. 观察者模式
观察者模式定义了一种一对多的依赖关系,当主题对象状态变化时,所有依赖它的观察者都会收到通知并自动更新。例如,在实时股票报价系统中,多个订阅者(观察者)可以注册到某个股票(主题),当股票价格变动时,系统自动通知所有订阅者更新显示。
例子:
Spring框架中确实使用了观察者模式来实现其事件驱动模型,具体体现在ApplicationEvent
和ApplicationListener
接口的设计上。下面是一个简化的描述:
-
org.springframework.context.ApplicationEvent
是Spring事件类的基类,代表了应用程序中发生的某种事件。Javapublic class ApplicationEvent extends EventObject { // 构造函数和其他相关方法... }
-
org.springframework.context.ApplicationListener
是事件监听器接口,任何实现了此接口的类都可以监听特定类型的ApplicationEvent。Java@FunctionalInterface public interface ApplicationListener<T extends ApplicationEvent> { void onApplicationEvent(T event); }
-
当某个事件发生时,例如在一个
ApplicationContext
上下文中,可以通过调用publishEvent()
方法发布事件,所有注册过的监听该事件类型的ApplicationListener
将会被通知并执行相应的方法。Java1// 在ApplicationContext中 2public void publishEvent(ApplicationEvent event) { 3 for (ApplicationListener<?> listener : getApplicationListeners()) { 4 if (listener instanceof SmartApplicationListener) { 5 SmartApplicationListener smartListener = (SmartApplicationListener) listener; 6 if (smartListener.supportsEventType(event.getClass())) { 7 smartListener.onApplicationEvent(event); 8 } 9 } else { 10 // Legacy listener, assuming it supports all events 11 listener.onApplicationEvent(event); 12 } 13 } 14}
10. 策略模式
策略模式是一种行为型设计模式。策略模式定义了一系列算法,并将每个算法封装为一个单独的类,使它们可以互换。客户端根据需求选择合适的策略。例如,在排序算法中,可以定义快速排序、归并排序、冒泡排序等策略,根据数据特性和性能要求在运行时动态选择。
例子:
在Spring数据源管理上使用了策略模式,允许用户根据不同的配置选择不同的数据源策略。例如,org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource
类就利用了策略模式,可以根据运行时条件动态切换数据源。
以下是一个抽象类的简化示例,展示了如何定义一个数据源路由策略接口:
Java
// 抽象类AbstractRoutingDataSource部分代码简化
public abstract class AbstractRoutingDataSource extends AbstractDataSource implements InitializingBean {
// ...其他代码...
private Object determineCurrentLookupKey() {
// 这里通常会委托给策略决定返回哪个数据源的键值
}
public Connection getConnection() throws SQLException {
DataSource dataSource = determineTargetDataSource();
// 根据策略获取实际的数据源并连接
return dataSource.getConnection();
}
protected DataSource determineTargetDataSource() {
// 这个方法可以由子类实现具体的策略逻辑
}
// ...其他代码...
}
在实际应用中,开发者需要重写 determineTargetDataSource
方法来实现数据源选择的策略。
11. 责任链模式
责任链模式是一种行为型设计模式,它将请求的发送者与接收者解耦,通过构建一系列处理对象形成一条链。每个处理对象(链中的节点)都包含对下一个节点的引用,并负责在接收到请求时决定是否对其进行处理,或者将请求传递给链中的下一个节点。这种模式允许请求沿着链路传递,直到找到能够处理它的节点为止。例如过滤器/拦截器链:Web应用中对HTTP请求进行逐级预处理和后处理。
例子:
在Spring MVC中,过滤器链(Filter Chain)和拦截器链(Interceptor Chain)使用了责任链模式。
12. 模板方法模式
模板方法模式是一种行为型设计模式,它在一个抽象类中定义了算法的骨架(模板方法),并将一些步骤延迟到子类中实现。模板方法封装了算法的整体结构,而子类可以通过继承这个抽象类并覆盖特定的步骤(方法)来实现对算法部分环节的个性化定制,而不改变算法的整体逻辑。例如应用框架初始化流程:如框架类定义启动、配置、初始化等步骤,具体实现由子类完成。
例子:
Springz的JDBC中JdbcTemplate
类就是一个模板方法模式的典型应用,它定义了一系列执行数据库操作的方法,如查询、更新、批处理等,这些方法内部调用抽象方法来完成具体的操作,而子类可以重写这些抽象方法来实现数据库驱动相关的逻辑。
Java
public abstract class JdbcTemplate extends JdbcAccessor implements JdbcOperations {
// ...
/**
* Execute a query given static SQL.
* <p>The results will be mapped to an ArrayList (one entry per row) of HashMaps
* (each representing a row, keyed by column name).
* @param sql the SQL query to execute
* @return a List that contains a Map per row
*/
public List<Map<String, Object>> queryForList(String sql) throws DataAccessException {
return query(sql, this::extractData, ResultSet::next);
}
// 抽象方法,留给子类实现
protected abstract <T> T doExecute(JdbcOperationsCallback<T> action) throws DataAccessException;
// 内部使用的回调方法,由模板方法调用
private <T> T query(String sql, RowMapper<T> rowMapper, ResultSetExtractor<Boolean> rowCallbackHandler) {
return doExecute((ConnectionCallback<T>) conn -> {
PreparedStatement ps = getPreparedStatement(conn, sql);
try {
return getJdbcOperations().query(ps, rowMapper, rowCallbackHandler);
}
finally {
closeStatement(ps);
}
});
}
// 子类可以覆盖这个方法来改变提取数据的行为
protected <T> T extractData(ResultSet rs) throws SQLException, DataAccessException {
throw new UnsupportedOperationException("Implementations must provide an implementation for this method");
}
// ...
}