16.Mybatis之mapper接口的代理对象生成
前提
当第一次访问 UserMapper.selectById(Long id) 时,spring 容器会根据 UserMapper 的类型去查缓存,找出对应的MapperFactoryBean 工厂实例 , 然后 spring 判断该 bean 实现了FactoryBean 接口,则不会直接返回该实例 ,而是调用该实例的 getObject()方法返回 UserMapper 的代理对象,下一次再访问 UserMapper 时,直接就是访问这个代理对象。
1.getObject()方法最终会走到这一步 , 代理对象的生成
return (T) Proxy.newProxyInstance(
mapperInterface.getClassLoader(),
new Class[] { mapperInterface },
mapperProxy
);
-
这是 JDK 动态代理的「底层 API」,必须满足一个前提:被代理的目标必须是接口(而 Mapper 恰好是接口,所以 MyBatis 只支持 JDK 代理,不用 CGLIB)。
-
mapperProxy:绑定「方法调用处理器」—— 当你调用代理对象的 selectById(1) 时,实际执行的是 mapperProxy.invoke(代理对象, selectById方法, 参数数组)。
-
代理对象的类型不是 UserMapperImpl(没有这个类),而是 JDK 动态代理生成的「匿名类」,但因为它实现了 UserMapper 接口,所以可以强转为 UserMapper 类型。
-
代码调用链
public T getObject() throws Exception { // 步骤1.1:调用SqlSession.getMapper(),传入Mapper接口 return getSqlSession().getMapper(this.mapperInterface); } // 辅助:获取Spring整合后的SqlSession(SqlSessionTemplate) @Override protected SqlSession getSqlSession() { return super.getSqlSession(); } public class SqlSessionTemplate implements SqlSession { private final SqlSessionFactory sqlSessionFactory; private final SqlSession sqlSessionProxy; // 实现SqlSession接口的getMapper方法 @Override public <T> T getMapper(Class<T> type) { // 步骤2.1:调用MyBatis原生Configuration的getMapper方法 return getConfiguration().getMapper(type, this); } // 辅助:获取MyBatis全局配置Configuration @Override public Configuration getConfiguration() { return this.sqlSessionFactory.getConfiguration(); } } public class Configuration { protected final MapperRegistry mapperRegistry = new MapperRegistry(this); // 调用MapperRegistry获取Mapper代理对象 public <T> T getMapper(Class<T> type, SqlSession sqlSession) { // 步骤3.1:转发到MapperRegistry的getMapper方法 return mapperRegistry.getMapper(type, sqlSession); } }
2.MapperRegistry里面的getMapper是代理对象生成的重点入口
```
public class MapperRegistry {
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();
public <T> void addMapper(Class<T> type) {
if (type.isInterface()) {
if (hasMapper(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
// 核心:为每个Mapper接口NEW一个专属的MapperProxyFactory
knownMappers.put(type, new MapperProxyFactory<>(type));
// 后续解析注解/XML...
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
// 判断是否已注册过该接口的MapperProxyFactory
public <T> boolean hasMapper(Class<T> type) {
return knownMappers.containsKey(type);
}
}
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
// 步骤4.1:根据Mapper接口获取对应的MapperProxyFactory(一对一绑定)
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
// 步骤4.2:调用MapperProxyFactory创建代理对象
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
```
-
knownMappers由谁初始化呢?-
如果在 mybatis-config.xml 中配置
,MyBatis 解析时会触发 addMapper(),为该接口创建 MapperProxyFactory 并放入 knownMappers: <!-- mybatis-config.xml --> <mappers> <!-- 配置UserMapper接口 → 触发addMapper(UserMapper.class) --> <mapper class="com.example.mapper.UserMapper"/> </mappers>XMLConfigBuilder 解析
<mappers>标签时,调用 MapperRegistry.addMapper(UserMapper.class),执行:// MapperRegistry.addMapper() 核心 knownMappers.put(UserMapper.class, new MapperProxyFactory<>(UserMapper.class)); -
唯一性:Map<Class, MapperProxyFactory> knownMappers 中,不同的 Mapper 接口对应完全不同的 MapperProxyFactory 实例。
-
MyBatis 初始化时的执行流程:
1.解析 UserMapper → 调用 addMapper(UserMapper.class) → 创建 MapperProxyFactory
实例 → 存入 knownMappers(Key:UserMapper.class,Value:该实例); 2.解析 OrderMapper → 调用 addMapper(OrderMapper.class) → 创建 MapperProxyFactory
实例 → 存入 knownMappers(Key:OrderMapper.class,Value:该实例);
-
-
MapperRegistry 的实例唯一归属并存储在 MyBatis 全局配置类 Configuration 中,是 Configuration 的一个核心成员变量;而 Configuration 最终存储在 SqlSessionFactory 中。
3.MapperProxy 来自 MapperProxyFactory
-
mapperProxyFactory.newInstance(sqlSession)该调用返回代理对象 , 每个Mapper接口占用一个专用的MapperProxyFactory <T>实例哦!public class MapperProxyFactory<T> { private final Class<T> mapperInterface; private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<>(); // 对外暴露的newInstance方法 public T newInstance(SqlSession sqlSession) { // 步骤5.1:创建MapperProxy(方法调用处理器) //这句话只执行一次,故而每个接口只有一个mapperProxy实例,而且是不同的接口对应不同的代理实例 //为什么?因为这是在创建代理对象呀,又不是在调用方法,一个接口的代理对象只创建一次,切记不要以为是直接调用接口方法走到这里的。 final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache); // 步骤5.2:调用重载方法生成代理对象 return newInstance(mapperProxy); } // 重载方法:生成JDK动态代理对象 protected T newInstance(MapperProxy<T> mapperProxy) { // 步骤5.3:JDK动态代理核心API,生成最终的Mapper代理对象 return (T) Proxy.newProxyInstance( mapperInterface.getClassLoader(), // 类加载器 new Class[]{mapperInterface}, // 实现的接口(目标Mapper) mapperProxy // 方法调用处理器 ); } }
5.最核心 MapperProxy
-
核心定位:MapperProxy 是 MyBatis 实现 Mapper 动态代理的核心处理器类,实现了 JDK 动态代理的 InvocationHandler 接口 —— 所有对 Mapper 代理对象的方法调用,最终都会转发到 MapperProxy 的 invoke() 方法中处理。
public class MapperProxy<T> implements InvocationHandler { private final SqlSession sqlSession; private final Class<T> mapperInterface; // 绑定唯一接口(如UserMapper.class) private final Map<Method, MapperMethod> methodCache; // 构造器:仅接收一个接口的Class对象 public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) { this.sqlSession = sqlSession; this.mapperInterface = mapperInterface; // 一旦初始化,无法修改 this.methodCache = methodCache; } } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { // 步骤1:过滤Object类的通用方法(toString/equals/hashCode等) if (Object.class.equals(method.getDeclaringClass())) { return method.invoke(this, args); } else { // 步骤2:处理Mapper接口方法(缓存+执行) return cachedInvoker(method).invoke(proxy, method, args, sqlSession); } } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } }
6.具体生成代理类逻辑
// JDK动态生成的匿名类(模拟)
public final class $Proxy89 implements UserMapper { // 自动实现UserMapper接口
// 持有InvocationHandler(即mapperProxy)
private final InvocationHandler h;
// 构造器:接收InvocationHandler
public $Proxy89(InvocationHandler h) {
this.h = h;
}
// 自动实现UserMapper的selectById方法
@Override
public User selectById(Integer id) {
try {
// 核心:把方法调用转发给mapperProxy.invoke()
Method method = UserMapper.class.getMethod("selectById", Integer.class);
return (User) h.invoke(this, method, new Object[] { id });
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
// 自动实现toString/equals等Object方法(略)
}
关键:这个匿名类是 JDK 自动生成的,它明确声明 implements UserMapper,所以能被强转为 UserMapper 类型 —— 你拿到的代理对象,本质就是这个类的实例。
结论
整个 MyBatis Mapper 动态代理的所有环节,最终都是为了让 MapperProxy 能拿到 SqlSession、methodCache、mapperInterface等核心工具,从而接管 Mapper 接口的方法调用,完成 SQL 执行和结果返回 ——MapperProxy 就是执行
这三个核心工具有啥用呢?
请看下回分解!!!

浙公网安备 33010602011771号