mybatis源码分析:Mapper接口是什么 mybatis源码分析
在《mybatis源码分析:启动过程》中分析了mybatis的启动过程,mybatis的启动过程主要集中在解析其核心配置文件(mybatis-config.xml)上,把配置文件中的配置全部解析到Configuration类中,每个配置在Configuration中均能找到其设置。本文分析mybatis中的查询接口(例,UserMapper)。 一、概述在编写mybatis的程序时,常见的做法时编写一个Mapper接口,再编写相应的映射文件,之后便可以初始化mybatis的环境,调用该接口的方法执行操作数据库的各中操作。那么Mapper接口是什么对象那,是怎样和映射文件关联,最后又怎样执行方法的。今天先分析Mapper接口,具体的环境可参考《mybatis源码分析:启动过程》中的相关代码。这里先给出一个结论,Mapper接口是使用JDK代理生成的一个代理类。 二、详述上面说到Mapper接口是使用JDK动态代理生成的一个代理类,下面通过代码去分析,下面是我测试的代码片段, package cn.com.mybatis.test; import java.io.IOException; java.io.InputStream; java.util.HashMap; java.util.List; org.apache.ibatis.io.Resources; org.apache.ibatis.session.SqlSession; org.apache.ibatis.session.SqlSessionFactory; org.apache.ibatis.session.SqlSessionFactoryBuilder; cn.com.mybatis.dao.UserMapper; public class TestMybatis { static void main(String[] args) throws IOException { // TODO Auto-generated method stub InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml"); System.out.println(ClassLoader.getSystemClassLoader()); System.out.println(Resources..getClassLoader()); SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); SqlSessionFactory factory = builder.build(inputStream); SqlSession session=factory.openSession(); UserMapper userMapper=session.getMapper(UserMapper.); session.insert(""); System.out.println("userMapper:"+userMapper); userMapper.getUser(); } } 上面从SqlSession中调用getMapper方法,传入一个XXMapper.class的参数,这里传入UserMapper.class(必须是接口 ?),返回的是一个UserMapper的对象,那么这个返回的值到底是什么那,我们分析源码。我们值得这里的session是一个DefaultSqlSession对象,看其方法, @Override public <T> T getMapper(Class<T> type) { return configuration.<T>getMapper(type,this); } 调用了Configuration对象的getMapper方法, type,SqlSession sqlSession) { return mapperRegistry.getMapper(type,sqlSession); } 调用了mapperRegistry的getMapper方法, @SuppressWarnings("unchecked") 根据Class对象获得一个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 { 生成一个目标对象的JDK代理对象,并返回 mapperProxyFactory.newInstance(sqlSession); } catch (Exception e) { new BindingException("Error getting mapper instance. Cause: " + e,e); } } 从上面可以看到从knownsMappers中根据type返回一个MapperProxyFactory,直译过来就是Mapper接口的代理工厂,mybatis的无处不在的工厂模式啊。 下面看knownsMappers的定义, 从上面可以看出knownMappers中使用map存储,其值为MapperProxyFactory,看下MapperProxyFactory的定义, 从上面可以看到该类有一个构造方法,该构造方法的参数也是一个Class对象,参数名称为mapperInterface,从方法名直译过来就是我们写的Mapper接口。 上面了解了knownsMappers和MapperProxyFactory后,可以知道knownsMappers中的value是根据Mapper接口生成的,那么取出来的值必为当前key的一个MapperProxyFactory,即该对象中的mapperInterface为key值。 根据Class取出其对应的MapperProxyFactory后,下面调用其newInstance方法,如下 下面看其newInstance方法, public T newInstance(SqlSession sqlSession) { final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession,mapperInterface,methodCache); newInstance(mapperProxy); } 生成一个MapperProxy对象, public MapperProxy(SqlSession sqlSession,Class<T> mapperInterface,Map<Method,MapperMethod> methodCache) { this.sqlSession = sqlSession; this.mapperInterface = mapperInterface; this.methodCache = methodCache; } 上面仅是给三个字段赋值,再看后面的方法,MapperProxyFactory的newInstance(mapperProxy)方法, @SuppressWarnings("unchecked"protected T newInstance(MapperProxy<T> mapperProxy) { JDK动态代理 return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(),1)"> Class[] { mapperInterface },mapperProxy); } 从上面可以看到是标准的JDK动态代理生成对象的放式,不再赘述。从这里看出返回的是一个Mapper接口的动态代理对象,第三个参数mapperProxy必然实现了InvocationHandler接口,看其定义 可以看到实现了InvocationHandler接口,那么在调用Mapper接口的时候肯定会执行MapperProxy的invoke方法,这里执行过程下次再分析。 三、总结本文分析了mybatis中Mapper接口的类型,即从mybatis中取出时是什么类型,这里是一个JDK的动态代理,所以我们要写的是一个接口,因为JDK动态代理是基于接口生成一个代理实现类。 ? 原创不易,有不正之处欢迎指正。 ? (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |