MyBatis作为一款优秀的ORM框架,其核心设计思想是通过动态代理和注解将接口方法与SQL操作解耦。开发者只需定义Mapper接口并添加注解,便能实现数据库操作,这背后隐藏着精妙的动态代理机制与源码设计。本文将从源码层解析MyBatis如何实现这一过程。
关键点:MyBatis通过JDK动态代理为Mapper接口生成代理对象,拦截所有方法调用,将其路由到SQL执行逻辑。
// 用户定义的Mapper接口
public interface UserMapper {
@Select("SELECT * FROM user WHERE id = #{id}")
User selectById(int id);
}
// 获取Mapper代理对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
源码入口:SqlSession#getMapper()
调用MapperRegistry.getMapper()
,最终通过MapperProxyFactory
创建代理。
// MapperProxyFactory核心代码
public class MapperProxyFactory<T> {
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[]{mapperInterface}, mapperProxy);
}
}
InvocationHandler
接口,拦截所有方法调用。当调用userMapper.selectById(1)
时,实际进入MapperProxy.invoke()
:
// MapperProxy#invoke 简化逻辑
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.isDefault()) { /* 处理默认方法 */ }
// 将方法封装为MapperMethod对象
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}
关键流程:启动时解析Mapper接口的注解,生成MappedStatement
,存储SQL和映射信息。
MapperAnnotationBuilder
类负责解析接口方法上的注解:
// MapperAnnotationBuilder#parse 核心逻辑
public void parse() {
for (Method method : type.getMethods()) {
parseStatement(method); // 解析每个方法
}
}
private void parseStatement(Method method) {
final SqlCommandType sqlCommandType = getSqlCommandType(method);
// 解析@Select、@Insert等注解
final String sql = getSqlAnnotation(method);
// 构建MappedStatement
builderAssistant.addMappedStatement(/* ... */);
}
每个方法对应一个MappedStatement
,包含:
根据SQL类型调用SqlSession
对应方法:
// MapperMethod#execute 简化逻辑
public Object execute(SqlSession sqlSession, Object[] args) {
switch (command.getType()) {
case SELECT:
if (method.returnsVoid()) { /* ... */ }
else {
Object param = method.convertArgsToSqlCommandParam(args);
return sqlSession.selectOne(command.getName(), param);
}
case INSERT: /* ... */
}
}
ParamNameResolver
解析方法参数名(支持@Param注解)。SqlSource
解析包含
,
等标签的SQL,生成可执行的SQL字符串。核心类:DefaultResultSetHandler
// 结果映射核心逻辑
List<Object> resultList = new ArrayList<>();
while (rs.next()) {
Object resultObject = createResultObject(rs, resultMap, lazyLoader);
// 通过反射或TypeHandler填充属性
applyPropertyMappings(rs, resultObject);
resultList.add(resultObject);
}
return resultList;
通过动态代理和注解,MyBatis实现了接口方法与SQL操作的优雅映射:
MapperProxy
拦截方法调用,路由到SqlSession
。MappedStatement
,存储SQL元数据。这种设计在保持灵活性的同时,极大简化了数据库操作代码,体现了MyBatis“约定优于配置”的核心思想。
源码分析要点:
MapperProxyFactory
、MapperProxy
MapperAnnotationBuilder
MapperMethod
、SqlSessionTemplate
DefaultResultSetHandler
通过深入源码,开发者可以更好地理解MyBatis的设计哲学,并针对复杂场景进行定制优化。