Mapper 接口无实现类动态代理原理
关于 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
关联知识库
推荐指数:
真诚点赞 诚不我欺~