




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、Mybatis接口编程原理分析Mybatis接口编程相关的类主要有MapperProxyFactory、MapperProxy、MapperMethod和MapperRegister四个类:MapperProxyFactory:通过类名我们可以猜到这是一个MapperProxy的工厂类,用于创建MapperProxy的,通过函数newInstance(SqlSession sqlSession)来创建MapperProxy的代理类。源码如下:java view plain copy print?在CODE上查看代码片派生到我的代码片/这个类赋值创建具体mapper接口代理对象的工厂 publi
2、c class MapperProxyFactory<T> /具体Mapper接口的class对象 private final Class<T> mapperInterface; /该接口下面的缓存,key是方法对象,value是对接口中方法对象的封装 private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>(); public MapperProxyFactory(Class<T> mapperI
3、nterface) this.mapperInterface = mapperInterface; public Class<T> getMapperInterface() return mapperInterface; public Map<Method, MapperMethod> getMethodCache() return methodCache; SuppressWarnings("unchecked") protected T newInstance(MapperProxy<T> mapperProxy) /创建一个代理类并
4、返回 return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class mapperInterface , mapperProxy); public T newInstance(SqlSession sqlSession) /在这里创建MapperProxy对象,这个类实现了JDK的动态代理接口 InvocationHandler final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInte
5、rface, methodCache); /调用上面的方法,返回一个接口的代理类 return newInstance(mapperProxy); MapperProxy:通过类名可以猜到这个类为一个代理类,它实现了JDK动态代理接口InvocationHandler,通过查看源码发现它又不像一个真正的代理类,它一般不会真正执行被代理类的函数方法,只是在执行被代理类函数方法时来执行MapperMethod类的execute方法,具体逻辑详看invoke函数源码如下:java view plain copy print?在CODE上查看代码片派生到我的代码片/实现了JDK动态代理的接口,Invo
6、cationHandler /在invoke方法中实现了代理方法调用的细节 public class MapperProxy<T> implements InvocationHandler, Serializable private static final long serialVersionUID = -6424540398559729838L; /sqlSession private final SqlSession sqlSession; /接口的类型对象 private final Class<T> mapperInterface; /接口中方法的缓存,由Ma
7、pperProxyFactory传递过来 private final Map<Method, MapperMethod> methodCache; /构造函数 public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) this.sqlSession = sqlSession; this.mapperInterface = mapperInterface; this.methodCache = method
8、Cache; /接口代理对象所有的方法调用都会调用该方法 Override public Object invoke(Object proxy, Method method, Object args) throws Throwable /判断是不是基础方法比如toString、hashCode等,这些方法直接调用不需要处理 if (Object.class.equals(method.getDeclaringClass() try return method.invoke(this, args); catch (Throwable t) throw ExceptionUtil.unwrapTh
9、rowable(t); /进行缓存 final MapperMethod mapperMethod = cachedMapperMethod(method); /调用mapperMethod.execute 核心的地方就在这个方法里,这个方法对才是真正对SqlSession进行的包装调用 return mapperMethod.execute(sqlSession, args); /缓存处理 private MapperMethod cachedMapperMethod(Method method) MapperMethod mapperMethod = methodCache.get(met
10、hod); if (mapperMethod = null) mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration(); methodCache.put(method, mapperMethod); return mapperMethod; MapperMethod:它是mybatis接口编程的核心,它封装了对sqlsession的操作,mybatis接口编程的实质还是sqlsession进行的CRUD操作通过分析下面的源码我们可以了解到mybatis接口编程的实质还是sqlse
11、ssion进行CRUD操作,接口编程不过是通过代理获得接口和函数名作为xml文件中的映射ID,获得接口函数的参数值作为sqlsession操作的参数值而已,所以mybatis的接口编程原理还是比较简单的。源码如下:java view plain copy print?/* * author Clinton Begin * author Eduardo Macarron * author Lasse Voss */ /这个类是整个代理机制的核心类,对Sqlsession当中的操作进行了封装 public class MapperMethod /一个内部封 封装了SQL标签的类型 insert u
12、pdate delete select private final SqlCommand command; /一个内部类 封装了方法的参数信息 返回类型信息等 private final MethodSignature method; /构造参数 public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) mand = new SqlCommand(config, mapperInterface, method); this.method = new MethodSignatu
13、re(config, method); /这个方法是对SqlSession的包装调用 public Object execute(SqlSession sqlSession, Object args) /定义返回结果 Object result; /如果是INSERT操作 if (SqlCommandType.INSERT = command.getType() /处理参数 Object param = method.convertArgsToSqlCommandParam(args); /调用sqlSession的insert方法 result = rowCountResult(sqlSes
14、sion.insert(command.getName(), param); /如果是UPDATE操作 同上 else if (SqlCommandType.UPDATE = command.getType() Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.update(command.getName(), param); /如果是DELETE操作 同上 else if (SqlCommandType.DELETE = command.getType()
15、Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.delete(command.getName(), param); /如果是SELECT操作 那么情况会多一些 但是也都和sqlSession的查询方法一一对应 else if (SqlCommandType.SELECT = command.getType() /如果返回void 并且参数有resultHandler /则调用 void select(String statement, Object para
16、meter, ResultHandler handler);方法 if (method.returnsVoid() && method.hasResultHandler() executeWithResultHandler(sqlSession, args); result = null; /如果返回多行结果这调用 <E> List<E> selectList(String statement, Object parameter); /executeForMany这个方法调用的 else if (method.returnsMany() result =
17、 executeForMany(sqlSession, args); /如果返回类型是MAP 则调用executeForMap方法 else if (method.returnsMap() result = executeForMap(sqlSession, args); else /否则就是查询单个对象 Object param = method.convertArgsToSqlCommandParam(args); result = sqlSession.selectOne(command.getName(), param); else if (SqlCommandType.FLUSH =
18、 command.getType() result = sqlSession.flushStatements(); else /如果全都不匹配 说明mapper中定义的方法不对 throw new BindingException("Unknown execution method for: " + command.getName(); /如果返回值为空 并且方法返回值类型是基础类型 并且不是VOID 则抛出异常 if (result = null && method.getReturnType().isPrimitive() && !met
19、hod.returnsVoid() throw new BindingException("Mapper method '" + command.getName() + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ")."); return result; private Object rowCountResult(int rowCount) final Object re
20、sult; if (method.returnsVoid() result = null; else if (Integer.class.equals(method.getReturnType() | Integer.TYPE.equals(method.getReturnType() result = Integer.valueOf(rowCount); else if (Long.class.equals(method.getReturnType() | Long.TYPE.equals(method.getReturnType() result = Long.valueOf(rowCou
21、nt); else if (Boolean.class.equals(method.getReturnType() | Boolean.TYPE.equals(method.getReturnType() result = Boolean.valueOf(rowCount > 0); else throw new BindingException("Mapper method '" + command.getName() + "' has an unsupported return type: " + method.getRetur
22、nType(); return result; private void executeWithResultHandler(SqlSession sqlSession, Object args) MappedStatement ms = sqlSession.getConfiguration().getMappedStatement(command.getName(); if (void.class.equals(ms.getResultMaps().get(0).getType() throw new BindingException("method " + comman
23、d.getName() + " needs either a ResultMap annotation, a ResultType annotation," + " or a resultType attribute in XML so a ResultHandler can be used as a parameter."); Object param = method.convertArgsToSqlCommandParam(args); if (method.hasRowBounds() RowBounds rowBounds = method.e
24、xtractRowBounds(args); sqlSession.select(command.getName(), param, rowBounds, method.extractResultHandler(args); else sqlSession.select(command.getName(), param, method.extractResultHandler(args); /返回多行结果 调用sqlSession.selectList方法 private <E> Object executeForMany(SqlSession sqlSession, Object
25、 args) List<E> result; Object param = method.convertArgsToSqlCommandParam(args); /如果参数含有rowBounds则调用分页的查询 if (method.hasRowBounds() RowBounds rowBounds = method.extractRowBounds(args); result = sqlSession.<E>selectList(command.getName(), param, rowBounds); else /没有分页则调用普通查询 result = sqlS
26、ession.<E>selectList(command.getName(), param); / issue #510 Collections & arrays support if (!method.getReturnType().isAssignableFrom(result.getClass() if (method.getReturnType().isArray() return convertToArray(result); else return convertToDeclaredCollection(sqlSession.getConfiguration()
27、, result); return result; private <E> Object convertToDeclaredCollection(Configuration config, List<E> list) Object collection = config.getObjectFactory().create(method.getReturnType(); MetaObject metaObject = config.newMetaObject(collection); metaObject.addAll(list); return collection;
28、SuppressWarnings("unchecked") private <E> E convertToArray(List<E> list) E array = (E) Array.newInstance(method.getReturnType().getComponentType(), list.size(); array = list.toArray(array); return array; private <K, V> Map<K, V> executeForMap(SqlSession sqlSession,
29、Object args) Map<K, V> result; Object param = method.convertArgsToSqlCommandParam(args); if (method.hasRowBounds() RowBounds rowBounds = method.extractRowBounds(args); result = sqlSession.<K, V>selectMap(command.getName(), param, method.getMapKey(), rowBounds); else result = sqlSession.&
30、lt;K, V>selectMap(command.getName(), param, method.getMapKey(); return result; public static class ParamMap<V> extends HashMap<String, V> private static final long serialVersionUID = -2212268410512043556L; Override public V get(Object key) if (!super.containsKey(key) throw new Binding
31、Exception("Parameter '" + key + "' not found. Available parameters are " + keySet(); return super.get(key); /一个内部类 封装了具体执行的动作 public static class SqlCommand /xml标签的id private final String name; /insert update delete select的具体类型 private final SqlCommandType type; public Sq
32、lCommand(Configuration configuration, Class<?> mapperInterface, Method method) /拿到全名 接口名加上方法名 String statementName = mapperInterface.getName() + "." + method.getName(); MappedStatement ms = null; /获取MappedStatement对象 这个对象封装了XML当中一个标签的所有信息 比如下面 /<select id="selectBlog" re
33、sultType="Blog"> /select * from Blog where id = #id /</select> if (configuration.hasStatement(statementName) ms = configuration.getMpedStatement(statementName); else if (!mapperInterface.equals(method.getDeclaringClass() / issue #35 String parentStatementName = method.getDeclaring
34、Class().getName() + "." + method.getName(); if (configuration.hasStatement(parentStatementName) ms = configuration.getMappedStatement(parentStatementName); /为空抛出异常 if (ms = null) if(method.getAnnotation(Flush.class) != null) name = null; type = SqlCommandType.FLUSH; else throw new BindingE
35、xception("Invalid bound statement (not found): " + statementName); else name = ms.getId(); type = ms.getSqlCommandType(); /判断SQL标签类型 未知就抛异常 if (type = SqlCommandType.UNKNOWN) throw new BindingException("Unknown execution method for: " + name); public String getName() return name;
36、 public SqlCommandType getType() return type; /内部类 封装了接口当中方法的 参数类型 返回值类型 等信息 public static class MethodSignature /是否返回多调结果 private final boolean returnsMany; /返回值是否是MAP private final boolean returnsMap; /返回值是否是VOID private final boolean returnsVoid; /返回值类型 private final Class<?> returnType; /m
37、apKey private final String mapKey; /resultHandler类型参数的位置 private final Integer resultHandlerIndex; /rowBound类型参数的位置 private final Integer rowBoundsIndex; /用来存放参数信息 private final SortedMap<Integer, String> params; /是否存在命名参数 private final boolean hasNamedParameters; public MethodSignature(Config
38、uration configuration, Method method) this.returnType = method.getReturnType(); this.returnsVoid = void.class.equals(this.returnType); this.returnsMany = (configuration.getObjectFactory().isCollection(this.returnType) | this.returnType.isArray(); this.mapKey = getMapKey(method); this.returnsMap = (t
39、his.mapKey != null); this.hasNamedParameters = hasNamedParams(method); this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class); this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class); this.params = Collections.unmodifiableSortedMap(getParams(method, this.hasNamedParame
40、ters); public Object convertArgsToSqlCommandParam(Object args) final int paramCount = params.size(); if (args = null | paramCount = 0) return null; else if (!hasNamedParameters && paramCount = 1) return argsparams.keySet().iterator().next().intValue(); else final Map<String, Object> pa
41、ram = new ParamMap<Object>(); int i = 0; for (Map.Entry<Integer, String> entry : params.entrySet() param.put(entry.getValue(), argsentry.getKey().intValue(); / issue #71, add param names as param1, param2.but ensure backward compatibility final String genericParamName = "param"
42、 + String.valueOf(i + 1); if (!param.containsKey(genericParamName) param.put(genericParamName, argsentry.getKey(); i+; return param; public boolean hasRowBounds() return rowBoundsIndex != null; public RowBounds extractRowBounds(Object args) return hasRowBounds() ? (RowBounds) argsrowBoundsIndex : nu
43、ll; public boolean hasResultHandler() return resultHandlerIndex != null; public ResultHandler extractResultHandler(Object args) return hasResultHandler() ? (ResultHandler) argsresultHandlerIndex : null; public String getMapKey() return mapKey; public Class<?> getReturnType() return returnType;
44、 public boolean returnsMany() return returnsMany; public boolean returnsMap() return returnsMap; public boolean returnsVoid() return returnsVoid; private Integer getUniqueParamIndex(Method method, Class<?> paramType) Integer index = null; final Class<?> argTypes = method.getParameterTypes(); for (int i = 0; i < argTypes.length; i+) if (paramType.isAssignableFrom(argTypesi) if (index = null) index = i; else throw new Bindin
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 长春大学旅游学院《数理方程二》2023-2024学年第二学期期末试卷
- 江苏省泰州市泰兴市2025届初三3月统一测试(一模)数学试题含解析
- 杨凌职业技术学院《唐诗和唐宋词经典导读》2023-2024学年第二学期期末试卷
- 山东省广饶县2024-2025学年3月初三教学测试(一)化学试题含解析
- 山东省聊城冠县联考2024-2025学年初三物理试题第一次诊断性检测试题含解析
- 江苏扬州2024-2025学年数学五下期末联考试题含答案
- 嘉应学院《护理与医疗保健》2023-2024学年第一学期期末试卷
- 山东省枣庄市第三十二中学2024-2025学年初三下学期第一次质量检测试题(数学试题理)试题含解析
- 上阴影的课件
- 山西同文职业技术学院《高级医学统计学》2023-2024学年第二学期期末试卷
- 2025年吉林工程职业学院单招职业技能考试题库附答案
- 日语专业的毕业论文
- 膏方基本知识
- T-GDEIIA 56-2024 垂直起降低空航空器起降场基础设施配置技术要求
- 《如何科学减重》课件
- 第1课时 收获的季节(教学设计)-2024-2025学年一年级上册数学北师大版
- 整本书阅读《林海雪原》【知识精研】六年级语文下册 (统编版五四制2024)
- 建筑工程项目管理经验分享
- 科技与金融结合的对公金融服务营销实践案例分析
- Unit 4 Writing说课稿 -2024-2025学年沪教版(五四制)英语六年级上册001
- 消防公务车管理制度内容
评论
0/150
提交评论