Mybatis中使用的设计模式解析
设计模式是针对常见软件设计问题的可重用解决方案。在MyBatis框架中,多种设计模式被应用于不同场景,以下是对其中几种核心设计模式的具体分析。
1. Builder模式
作用:用于分步构建复杂的配置对象,避免构造函数过于庞大。
MyBatis中的应用实例:
框架初始化时,通过多个Builder类解析XML配置文件与映射文件。
SqlSessionFactoryBuilder:构建核心工厂类SqlSessionFactoryXMLConfigBuilder:解析全局配置文件XMLMapperBuilder:解析Mapper映射文件XMLStatementBuilder:解析SQL语句定义
优势:将复杂对象的构造过程进行封装,使代码结构清晰,并降低调用方的使用难度。
// Builder模式的使用示例
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
// SqlSessionFactoryBuilder就是Builder模式的典型应用
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
底层实现:
// 以SqlSessionFactoryBuilder为例
public class SqlSessionFactoryBuilder {
public SqlSessionFactory build(InputStream inputStream) {
return build(inputStream, null, null);
}
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
// 使用XMLConfigBuilder解析配置
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
// 最终构建SqlSessionFactory
return build(parser.parse());
} finally {
ErrorContext.instance().reset();
}
}
}
2. 工厂模式
作用:将对象创建逻辑封装在专门类中,实现创建与使用的分离。
MyBatis中的应用实例:
SqlSessionFactory:负责创建MyBatis核心会话对象SqlSessionObjectFactory:负责创建结果集对象的实例MapperProxyFactory:专用于生成Mapper接口的代理实例
优势:集中管理对象创建逻辑,提高系统可扩展性。例如,通过自定义ObjectFactory可以改变实体类的实例化方式。
// 工厂模式的使用
SqlSessionFactory factory = sqlSessionFactoryBuilder.build(inputStream);
// SqlSessionFactory创建SqlSession
SqlSession session = factory.openSession();
// 获取Mapper接口的代理对象(背后使用MapperProxyFactory)
UserMapper userMapper = session.getMapper(UserMapper.class);
3. 单例模式
作用:确保类在运行期间只有一个实例,并提供全局访问点。
MyBatis中的应用实例:
ErrorContext:基于ThreadLocal实现线程内单例,维护当前线程中的错误上下文信息LogFactory:作为日志组件适配器的工厂,内部维护日志实现实例
优势:减少重复实例创建,节省系统资源,同时保证特定场景下上下文信息的一致性。
// ErrorContext的单例实现
public class ErrorContext {
private static final ThreadLocal<ErrorContext> LOCAL = new ThreadLocal<>();
private ErrorContext() {} // 私有构造函数
public static ErrorContext instance() {
ErrorContext context = LOCAL.get();
if (context == null) {
context = new ErrorContext();
LOCAL.set(context);
}
return context;
}
// 使用示例:在解析过程中记录错误信息
public void recordError(String activity, String objectType, String objectId) {
this.activity = activity;
this.objectType = objectType;
this.objectId = objectId;
}
}
4. 代理模式
作用:通过代理对象控制对原始对象的访问,常用于功能增强。
MyBatis中的应用实例:
MapperProxy:基于JDK动态代理实现,将Mapper接口的方法调用转发给实际的SQL执行ConnectionLogger:作为连接对象的代理,提供连接活动的日志记录- 延迟加载实现:使用CGLIB或Javassist生成代理对象,在真正访问属性时才触发数据库查询
优势:无需修改原始代码即可增强功能,是实现MyBatis核心机制的关键技术。
// MapperProxy的简化实现
public class MapperProxy<T> implements InvocationHandler, Serializable {
private final SqlSession sqlSession;
private final Class<T> mapperInterface;
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 如果是Object类的方法,直接调用
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
}
// 将方法调用转换为SQL执行
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}
}
// 使用示例 - 我们调用Mapper接口方法时,实际上调用的是代理对象
public interface UserMapper {
User getUserById(Long id);
}
// 实际调用过程:
UserMapper mapper = session.getMapper(UserMapper.class);
// 下面的调用实际上被MapperProxy拦截处理
User user = mapper.getUserById(1L);
5. 组合模式
作用:将对象组织成树形结构,以统一方式处理单个对象和对象组合。
MyBatis中的应用实例:
动态SQL解析过程中的SqlNode接口体系:
- 基础接口
SqlNode - 具体实现类
IfSqlNode、WhereSqlNode、ChooseSqlNode等 - 容器类
MixedSqlNode,可包含多个子SqlNode
优势:使得复杂的动态SQL解析可以通过递归方式简洁实现,无论SQL结构如何嵌套,处理逻辑都保持一致。
// SqlNode接口定义
public interface SqlNode {
boolean apply(DynamicContext context);
}
// 具体实现 - IfSqlNode
public class IfSqlNode implements SqlNode {
private final ExpressionEvaluator evaluator;
private final String test;
private final SqlNode contents;
public IfSqlNode(SqlNode contents, String test) {
this.contents = contents;
this.test = test;
}
@Override
public boolean apply(DynamicContext context) {
// 评估test表达式,决定是否应用该SQL片段
if (evaluator.evaluateBoolean(test, context.getBindings())) {
contents.apply(context);
return true;
}
return false;
}
}
// 组合节点 - MixedSqlNode
public class MixedSqlNode implements SqlNode {
private final List<SqlNode> contents;
public MixedSqlNode(List<SqlNode> contents) {
this.contents = contents;
}
@Override
public boolean apply(DynamicContext context) {
// 遍历所有子节点并应用
for (SqlNode sqlNode : contents) {
sqlNode.apply(context);
}
return true;
}
}
// 对应XML配置示例
// <select id="findUser">
// SELECT * FROM users
// <where>
// <if test="name != null">AND name = #{name}</if>
// <if test="age != null">AND age = #{age}</if>
// </where>
// </select>
总结
通过上述代码示例,我们可以更清晰地看到:
- Builder模式 通过分步构建来处理复杂的配置解析
- 工厂模式 统一管理核心对象的创建过程
- 单例模式 确保上下文信息在线程内的唯一性
- 代理模式 实现Mapper接口方法到SQL执行的转换
- 组合模式 优雅处理动态SQL的嵌套结构
这些设计模式的合理运用,使得MyBatis在保持代码清晰的同时,具备了良好的扩展性和维护性。理解这些模式的具体实现,有助于我们在自己的项目中更好地应用这些设计思想。

浙公网安备 33010602011771号