Mapper 接口无实现类动态代理原理

青苗 青苗 | 3141 | 2022-08-24

关于 Mybatis 核心 mapper 无具体实现如何执行 SQL 这部分相信很多人非常好奇,本文主要通过一个例子讲清楚这部分的原理实现。

核心原理 JDK InvocationHandler 动态代理,这部分我们需要熟悉 Proxy.newProxyInstance 如何生成代理类,这里就不逐一说明自行查找资料补全这部分知识点。

本文基于 JDK 18 演示例子

Mapper 代理类 MapperProxy

import java.io.Serializable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Map;

public record MapperProxy<T>(Map<Method, MapperMethod> methodCache) implements InvocationHandler, Serializable {

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (Object.class.equals(method.getDeclaringClass())) {
            try {
                return method.invoke(this, args);
            } catch (Throwable t) {
                t.printStackTrace();
            }
        }
        return cachedMapperMethod(method).execute(args);
    }

    private MapperMethod cachedMapperMethod(Method method) {
        MapperMethod mapperMethod = methodCache.get(method);
        if (mapperMethod == null) {
            mapperMethod = new MapperMethod(method, this);
            methodCache.put(method, mapperMethod);
        }
        return mapperMethod;
    }
}

代理工厂 MapperProxyFactory


import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class MapperProxyFactory<T> {
    private final Class<T> mapperInterface;
    private final Map<Method, MapperMethod> mapperMethodCache = new ConcurrentHashMap<>();

    public MapperProxyFactory(Class<T> mapperInterface) {
        this.mapperInterface = mapperInterface;
    }

    protected T newInstance(MapperProxy<T> mapperProxy) {
        return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[]{mapperInterface}, mapperProxy);
    }

    public T newInstance() {
        return newInstance(new MapperProxy<>(mapperMethodCache));
    }
}

Mapper 注册类 MapperRegistry

public class MapperRegistry {

    public <T> T newInstance(Class<T> type) {
        MapperProxyFactory<T> userDaoProxyFactory = new MapperProxyFactory(type);
        return userDaoProxyFactory.newInstance();
    }
}

Sql 注解

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Sql {

    String value();
}

测试类 TestMapper

public interface TestMapper {

    @Sql("select name from test where name=%d")
    String selectNameById(int id);
}

测试案例

@Test
public void testMapperProxy() {
    MapperRegistry mapperRegistry = new MapperRegistry();
    TestMapper testMapper = mapperRegistry.newInstance(TestMapper.class);
    String name = testMapper.selectNameById(1);
    System.out.println("name = " + name);
}

输出结果

execute method : selectNameById
execute sql : select name from test where name=1
name = aizuda.com

关联知识库

MybatisPlus 杂谈
文章标签: MybatisPlus
推荐指数:

真诚点赞 诚不我欺~

Mapper 接口无实现类动态代理原理

点赞 收藏 评论

关于作者

青苗
青苗

青苗幼儿园园长

等级 LV5

粉丝 19

获赞 47

经验 1152

关联知识库

MybatisPlus 杂谈