种一棵树最好的时间是十年前,其次是现在;
Mybatis中声明一个interface接口(如代码所示),没有编写任何实现类,Mybatis就能返回接口实例,并调用接口方法返回数据库数据,你知道为什么不?
public interface UserMapper {
User getUser(Integer userId);
List<User> getAllUser();
Map findName();
}
其中使用的就是动态代理;
动态代理的功能:通过拦截器方法回调,对目标target方法进行增强。
1、自定义增强mapper
自己实现一个 InvocationHandler ;
/**
* @author zhaojm
* @date 2020/4/11 21:29
*/
public class MapperProxy implements InvocationHandler {
// 新建一个增强Mapper
@SuppressWarnings("unchecked")
public <T> T newInstance(Class<T> clz) {
return (T) Proxy.newProxyInstance(clz.getClassLoader(), new Class[]{clz}, this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// Object.class 中的方法 Object
if(Object.class.equals(method.getDeclaringClass())){
try {
return method.invoke(this, args);
} catch (Throwable t) {
}
}
// 请求
return new User((Integer)args[0], "matt", 24);
}
自己的mapperProxy 测试
public static void main(String[] args) {
MapperProxy proxy = new MapperProxy();
// 增强 UserMapper => MapperProxy
UserMapper mapper = proxy.newInstance(UserMapper.class);
// 调用 MapperProxy.invoke
User user = mapper.getUser(2);
System.out.print(user.getUserId());
System.out.print(user.getUsername());
System.out.print(user.getAge());
System.out.println();
// object 方法 调用的是Object.tostring
// (此时已经是增强类了 MapperProxy)
System.out.println(mapper.toString());
}
}
输出
# getUser方法是Inte
2 matt 24
# object
org.mybatis.internal.example.handler.MapperProxy@610455d6
2、MyBatis MapperProxy
我们再来看下 Mybatis 如何实现的
2.1、测试类
public static void main(String[] args) {
String resource = "org/mybatis/internal/example/Configuration.xml";
Reader reader;
try {
// mybatis 初始化
reader = Resources.getResourceAsReader(resource);
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader);
try (SqlSession sqlSession = factory.openSession()) {
// 1、增强 UserMapper => MapperProxy
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 2、增强类拦截请求,并执行sql
List<User> userList = userMapper.getAllUser();
for (User user : userList) {
System.out.println(user);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
2.2 MapperProxyFactory 增强 UserMapper => MapperProxy
实现的起点:sqlSession.getMapper(UserMapper.class);
调用 MapperProxyFactory 创建 UserMapper 代理器
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else if (isDefaultMethod(method)) {
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
// 截断请求,执行sql
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}
2.3 MapperProxy 增强类拦截请求,并执行sql
这个地方对比自己写的增强方法
org.apache.ibatis.binding.MapperProxy.java 部分源码
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else if (isDefaultMethod(method)) {
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
// 截断请求,执行sql
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}
如同上述列子一样,userMapper 被增强为 MapperProxy 代理类,代理类接管了请求之后继续执行后续操作(查询返回结果集)
这样我们写的Mapper层的接口为什么不需要实现类与执行逻辑了。
以上是学习MyBatis 技术内幕时的总结,有关于Mybatis的相关总结会继续下去(立flag)如果有误欢迎留言指出。
种一棵树最好的时间是十年前,其次是现在;