Mybatis源码解析-MapperRegistry注册mapper接口
来源:互联网 发布:淘宝客开源cms 编辑:程序博客网 时间:2024/05/19 10:36
知识储备
- SqlsessionFactory-mybatis持久层操作数据的根本,具体的解析是通过
SqlSessionFactoryBean
生成的,具体的形成可见>>>Spring mybatis源码篇章-SqlSessionFactoryBean - MapperInterface-mybatis的java接口类,用于service/controller层的调用,具体的解析是通过
MapperScannerConfigurer
扫描接口并封装成MapperFactoryBean
来注册生成,可见>>>Spring mybatis源码篇章-sql mapper配置文件绑定mapper class类 - 保存mybatis的各种信息都是由
org.apache.ibatis.session.Configuration
类来维护的
MapperRegistry
由于mybatis与数据库进行通信需要保证其java访问类必须为接口类,所以我们必须了解其是怎么保存这些接口类的访问方式,入口由Configuration#addMapper()
方法调用,如下述MapperFactoryBean#checkConfig()
源码
@Override protected void checkDaoConfig() { super.checkDaoConfig(); notNull(this.mapperInterface, "Property 'mapperInterface' is required"); Configuration configuration = getSqlSession().getConfiguration(); if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) { try { //只关注此处的添加mapper接口 configuration.addMapper(this.mapperInterface); } catch (Throwable t) { throw new IllegalArgumentException(t); } finally { ErrorContext.instance().reset(); } } }
MapperRegistry#addMapper()-创建mapper访问代理
直接阅读源码
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接口包装成mapper代理 knownMappers.put(type, new MapperProxyFactory<T>(type)); //解析接口上的注解或者加载mapper配置文件生成mappedStatement MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type); parser.parse(); loadCompleted = true; } finally { if (!loadCompleted) { knownMappers.remove(type); } } } }
看下关于mapper的代理如何生成
MapperProxyFactory
内部属性概览
//被代理类 private final Class<T> mapperInterface; //支持对被代理类进行缓存 private Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>();
创建代理类
@SuppressWarnings("unchecked") protected T newInstance(MapperProxy<T> mapperProxy) { //采用JDK自带的Proxy代理模式生成 return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy); } public T newInstance(SqlSession sqlSession) { //MapperProxy为InvocationHandler的实现类 final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache); //真实生成代理 return newInstance(mapperProxy); }
接下来阅读下MapperProxy是怎么运用反射调用mapper接口类的方法
MapperProxy
直接看对JDK的proxy代理的实现方法
@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) { throw ExceptionUtil.unwrapThrowable(t); } } //尝试从缓存中获取,也就是看到的methodCache final MapperMethod mapperMethod = cachedMapperMethod(method); //通过MapperMethod对象调用方法 return mapperMethod.execute(sqlSession, args); } private MapperMethod cachedMapperMethod(Method method) { MapperMethod mapperMethod = methodCache.get(method); if (mapperMethod == null) { //注意此处的传的参数为mapper接口类、method对象、Configuration对象 mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()); methodCache.put(method, mapperMethod); } return mapperMethod; }
需要观察下MapperMethod是如何操作数据的
MapperMethod
构造函数
//Sql指令类 private final SqlCommand command; //方法签名类 private final MethodSignature method; public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) { this.command = new SqlCommand(config, mapperInterface, method); this.method = new MethodSignature(config, method); }
主要方法execute()
public Object execute(SqlSession sqlSession, Object[] args) {Object result;//CURD操作,对持久层返回的结果集进行处理if (SqlCommandType.INSERT == command.getType()) { //获取method方法上的带有@Param的参数,默认返回0,1,2,3... Object param = method.convertArgsToSqlCommandParam(args); //最终是通过sqlSession接口对象获取结果集,注意此处的command.getName()为mappedStatementId result = rowCountResult(sqlSession.insert(command.getName(), param));} else if (SqlCommandType.UPDATE == command.getType()) { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.update(command.getName(), param));} else if (SqlCommandType.DELETE == command.getType()) { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.delete(command.getName(), param));} else if (SqlCommandType.SELECT == command.getType()) { //查询语句的各种情况应对 if (method.returnsVoid() && method.hasResultHandler()) { executeWithResultHandler(sqlSession, args); result = null; } else if (method.returnsMany()) { result = executeForMany(sqlSession, args); } else if (method.returnsMap()) { result = executeForMap(sqlSession, args); } else { Object param = method.convertArgsToSqlCommandParam(args); result = sqlSession.selectOne(command.getName(), param); }} else { throw new BindingException("Unknown execution method for: " + command.getName());}return result; }
SqlCommand是MapperMethod的静态内部类,主要通过mapperInterface和method从Configuration中获取MappedStatement对象保存其id和type属性,供sqlsession持久层接口调用
MethodSignature是MappedMethod的静态内部类,主要对method对象的返回类型、参数等进行归类
MapperMethod的作用是处理SqlSession接口调用CRUD操作后产生的结果集
MapperRegistry#getMapper()-获取mapper代理类
源码如下
@SuppressWarnings("unchecked") public <T> T getMapper(Class<T> type, SqlSession sqlSession) { final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type); if (mapperProxyFactory == null) throw new BindingException("Type " + type + " is not known to the MapperRegistry."); try { //获取mapper代理类 return mapperProxyFactory.newInstance(sqlSession); } catch (Exception e) { throw new BindingException("Error getting mapper instance. Cause: " + e, e); } }
那么获取代理类这个操作是如何被调用的呢?这其实是bean工厂对其里面所有的beanDefinition进行实例化调用的,这点可查看>>>Spring源码情操陶冶-AbstractApplicationContext#finishBeanFactoryInitialization。而针对FactoryBean
接口的实例化会调用其中的getObject()
方法,所以我们看下MapperFactoryBean#getObject()
方法
public T getObject() throws Exception { //通过sqlSessionTemplate调用Configuration#getMapper()方法间接调用MapperRegistry#getMapper() return getSqlSession().getMapper(this.mapperInterface); }
小结
本文是对
MapperFactoryBean#checkDaoConfig()
方法的补充,通过对Mybatis的MapperRegistry
的分析我们可以得出以下结论:
mapperInterface的内部方法数据持久层访问是通过JDK的代理来完成的
MapperMethod是对上述代理的method方法的真实处理,主要是对sqlSession的返回结果集进行对应的整理输出,具体读者可自行查阅分析
sqlSession的CRUD肯定牵扯到MappedStatement对象的使用,后续我们着重分析下SqlSessionTemplate的源码加深我们对mybatis的理解
- Mybatis源码解析-MapperRegistry注册mapper接口
- Mybatis源码解析-MapperRegistry注册mapper接口
- Mybatis 源码解析三、Mapper接口与mapper.xml文件绑定
- Mybatis 源码解析二、Mapper接口的代理实现过程 MapperScannerConfigurer 解析
- mybatis mapper参数解析
- Type interface com.mybatis.mapper.UserMapper is not known to the MapperRegistry. 解决方法
- Type interface com.mybatis.mapper.StudentMapper is not known to the MapperRegistry
- Mybatis报错——Type interface com.mapper.EmployeeMapper is already known to the MapperRegistry.
- [mybatis]Mapper接口代理开发
- MyBatis Mapper接口实现原理
- [Mybatis] Mapper接口原理分析
- mybatis mapper接口代理开发
- mybatis 中的mapper接口问题
- Mybatis的MapperRegistry错误
- 【Mybatis源码剖析】Spring中获取 Mybatis Mapper接口(注解Autowired),并调用过程剖析
- mybatis源码解析之SqlSession接口。
- Mybatis源码分析获取Mapper
- mybatis配置文件(mapper)属性解析
- VMware Tools实现windows与linux的文件共享(原创) 原创 2012年06月24日 16:45:40 997925 在VMware虚拟机中安装好了VMware Tools,能实现主
- 【C#学习】公共语言运行库 CLR
- 如何实现下拉选择省份后,后续单元格能选择对应的城市和县区
- ui开源合集1
- JS函数的学习
- Mybatis源码解析-MapperRegistry注册mapper接口
- Android进阶#(6/12)让程序更优的技术——性能优化_性能优化
- Java不定参数Object… obj说明 .
- Android layout常见的属性大全
- JSP自定义标签(二):JSP传统标签
- 将PHP的SESSION数据存放到Redis中
- .NET Core 2.0 开源Office组件 NPOI
- 一个iOS程序员自己写代码将Kindle中我的剪贴内容筛选提取出来
- SQL注入学习路线