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 能拿到 SqlSessionmethodCachemapperInterface等核心工具,从而接管 Mapper 接口的方法调用,完成 SQL 执行和结果返回 ——MapperProxy 就是执行

这三个核心工具有啥用呢?

请看下回分解!!!

posted @ 2025-12-08 16:16  那就改变世界吧  阅读(0)  评论(0)    收藏  举报