MyBatis Mapper 接口如何通过JDK动态代理来包装SqlSession 源码分析

我们以往使用ibatis或者mybatis 都是以这种方式调用XML当中定义的CRUD标签来执行SQL 比如这样

 

[xml]  view plain  copy
  1. <?xml version="1.0" encoding="UTF-8" ?>  
  2. <!DOCTYPE mapper  
  3.   PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"  
  4.   "http://mybatis.org/dtd/mybatis-3-mapper.dtd">  
  5. <mapper namespace="org.mybatis.example.BlogMapper">  
  6.   <select id="selectBlog" resultType="Blog">  
  7.     select * from Blog where id = #{id}  
  8.   </select>  
  9. </mapper>  

 

[java]  view plain  copy
  1. SqlSession session = sqlSessionFactory.openSession();  
  2. try {  
  3.   Blog blog = (Blog) session.selectOne("org.mybatis.example.BlogMapper.selectBlog"101);  
  4. finally {  
  5.   session.close();  
  6. }  

 

 

这种方式有很明显的缺点就是通过字符串去调用标签定义的SQL,第一容易出错,第二是当XML当中的id修改过以后你不知道在程序当中有多少个地方使用了这个ID,需要手工查找并一一修改。在Mybatis这个版本中做了一些改进,支持这种方式调用。

定义一个接口 方法名,参数需要与XML定义保持一致。

[java]  view plain  copy
  1. org.mybatis.example.BlogMapper.selectBlog  
  2. public interface BlogMapper {  
  3.   Blog selectBlog(int id);  
  4. }  

然后这么调用,这样以来当我们修改了XML的ID以后,只需要修改接口中的方法就可以了,编译器会在其他使用该接口的地方报错,很容易进行修改。当然好处还不只这些,还可以通过与Spring进行无缝集成,动态注入 等等。后面会一一讲到。

[java]  view plain  copy
  1. SqlSession session = sqlSessionFactory.openSession();  
  2. try {  
  3.   BlogMapper mapper = session.getMapper(BlogMapper.class);  
  4.   Blog blog = mapper.selectBlog(101);  
  5. finally {  
  6.   session.close();  
  7. }  

 

本文的重点不是去讲解如何使用MyBatis(关于如何使用Mybatis可以参考官方API http://mybatis.github.io/mybatis-3/zh/getting-started.html),而是讲解MyBatis是如何通过接口对SqlSession进行动态封装的。

在上面的例子当中呢,BlogMapper是一个接口 它并没有实现类,为什么接口可以直接使用呢? 

那是因为MyBbatis使用了JDK动态代理机制动态生成了代理类,那么代理类又是如何多SqlSession进行封装的呢?

带着这些疑问,让我们通过分析源代码的方式来解释这些问题。

 

Mybatis关于包装Mapper的代码都在org.apache.ibatis.binding 这个包下面。

其中有4个类。

 

上面的4个类封装了Mapper接口动态生成代理类的全部细节

MapperRegistry 类是注册Mapper接口与获取代理类实例的工具类

 

[java]  view plain  copy
  1. package org.apache.ibatis.binding;  
  2. import org.apache.ibatis.builder.annotation.MapperAnnotationBuilder;  
  3. import org.apache.ibatis.io.ResolverUtil;  
  4. import org.apache.ibatis.session.Configuration;  
  5. import org.apache.ibatis.session.SqlSession;  
  6. import java.util.Collection;  
  7. import java.util.Collections;  
  8. import java.util.HashMap;  
  9. import java.util.Map;  
  10. import java.util.Set;  
  11. //这个类通过名字就可以看出 是用来注册Mapper接口与获取生成代理类实例的工具类   
  12. public class MapperRegistry {  
  13.   //全局配置文件对象  
  14.   private Configuration config;  
  15.   //一个HashMap Key是mapper的类型对象, Value是MapperProxyFactory对象  
  16.   //这个MapperProxyFactory是创建Mapper代理对象的工厂 我们一会在分析  
  17.   private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<Class<?>, MapperProxyFactory<?>>();  
  18.   public MapperRegistry(Configuration config) {  
  19.     this.config = config;  
  20.   }  
  21.   //获取生成的代理对象  
  22.   @SuppressWarnings("unchecked")  
  23.   public <T> T getMapper(Class<T> type, SqlSession sqlSession) {  
  24.     //通过Mapper的接口类型 去Map当中查找 如果为空就抛异常  
  25.     final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);  
  26.     if (mapperProxyFactory == null)  
  27.       throw new BindingException("Type " + type + " is not known to the MapperRegistry.");  
  28.     try {  
  29.       //否则创建一个当前接口的代理对象 并且传入sqlSession  
  30.       return mapperProxyFactory.newInstance(sqlSession);  
  31.     } catch (Exception e) {  
  32.       throw new BindingException("Error getting mapper instance. Cause: " + e, e);  
  33.     }  
  34.   }  
  35.     
  36.   public <T> boolean hasMapper(Class<T> type) {  
  37.     return knownMappers.containsKey(type);  
  38.   }  
  39.   //注册Mapper接口  
  40.   public <T> void addMapper(Class<T> type) {  
  41.     if (type.isInterface()) {  
  42.       if (hasMapper(type)) {  
  43.         throw new BindingException("Type " + type + " is already known to the MapperRegistry.");  
  44.       }  
  45.       boolean loadCompleted = false;  
  46.       try {  
  47.         knownMappers.put(type, new MapperProxyFactory<T>(type));  
  48.         // It's important that the type is added before the parser is run  
  49.         // otherwise the binding may automatically be attempted by the  
  50.         // mapper parser. If the type is already known, it won't try.  
  51.         MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);  
  52.         parser.parse();  
  53.         loadCompleted = true;  
  54.       } finally {  
  55.         if (!loadCompleted) {  
  56.           knownMappers.remove(type);  
  57.         }  
  58.       }  
  59.     }  
  60.   }  
  61.   public Collection<Class<?>> getMappers() {  
  62.     return Collections.unmodifiableCollection(knownMappers.keySet());  
  63.   }  
  64.     ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>();  
  65.     resolverUtil.find(new ResolverUtil.IsA(superType), packageName);  
  66.     Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses();  
  67.     for (Class<?> mapperClass : mapperSet) {  
  68.       addMapper(mapperClass);  
  69.     }  
  70.   }  
  71.   //通过包名扫描下面所有接口  
  72.   public void addMappers(String packageName) {  
  73.     addMappers(packageName, Object.class);  
  74.   }  
  75.     
  76. }  
  77. MapperRegistry  
[java]  view plain  copy
  1. package org.apache.ibatis.binding;  
  2. import java.lang.reflect.Method;  
  3. import java.lang.reflect.Proxy;  
  4. import java.util.Map;  
  5. import java.util.concurrent.ConcurrentHashMap;  
  6. import org.apache.ibatis.session.SqlSession;  
  7. //这个类负责创建具体Mapper接口代理对象的工厂类  
  8. public class MapperProxyFactory<T> {  
  9.   //具体Mapper接口的Class对象  
  10.   private final Class<T> mapperInterface;  
  11.   //该接口下面方法的缓存 key是方法对象 value是对接口中方法对象的封装  
  12.   private Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>();  
  13.   //构造参数没啥好说的  
  14.   public MapperProxyFactory(Class<T> mapperInterface) {  
  15.     this.mapperInterface = mapperInterface;  
  16.   }  
  17.   public Class<T> getMapperInterface() {  
  18.     return mapperInterface;  
  19.   }  
  20.   public Map<Method, MapperMethod> getMethodCache() {  
  21.     return methodCache;  
  22.   }  
  23.   @SuppressWarnings("unchecked")  
  24.   protected T newInstance(MapperProxy<T> mapperProxy) {  
  25.     //创建了一个代理类并返回  
  26.     //关于Proxy的API 可以查看java官方的API  
  27.     return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);  
  28.   }  
  29.   //在这里传入sqlSession 创建一个Mapper接口的代理类  
  30.   public T newInstance(SqlSession sqlSession) {  
  31.     //在这里创建了MapperProxy对象 这个类实现了JDK的动态代理接口 InvocationHandler  
  32.     final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);  
  33.     //调用上面的方法 返回一个接口的代理类  
  34.     return newInstance(mapperProxy);  
  35.   }  
  36. }  
  37. MapperProxyFactory  
[java]  view plain  copy
  1. package org.apache.ibatis.binding;  
  2. import java.io.Serializable;  
  3. import java.lang.reflect.InvocationHandler;  
  4. import java.lang.reflect.Method;  
  5. import java.util.Map;  
  6. import org.apache.ibatis.session.SqlSession;  
  7. //实现了JDK动态代理的接口 InvocationHandler  
  8. //在invoke方法中实现了代理方法调用的细节  
  9. public class MapperProxy<T> implements InvocationHandler, Serializable {  
  10.   private static final long serialVersionUID = -6424540398559729838L;  
  11.   //SqlSession  
  12.   private final SqlSession sqlSession;  
  13.   //接口的类型对象  
  14.   private final Class<T> mapperInterface;  
  15.   //接口中方法的缓存 有MapperProxyFactory传递过来的。  
  16.   private final Map<Method, MapperMethod> methodCache;  
  17.   //构造参数  
  18.   public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {  
  19.     this.sqlSession = sqlSession;  
  20.     this.mapperInterface = mapperInterface;  
  21.     this.methodCache = methodCache;  
  22.   }  
  23.   //接口代理对象所有的方法调用 都会调用该方法  
  24.   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  
  25.     //判断是不是基础方法 比如toString() hashCode()等,这些方法直接调用不需要处理  
  26.     if (Object.class.equals(method.getDeclaringClass())) {  
  27.       return method.invoke(this, args);  
  28.     }  
  29.     //这里进行缓存  
  30.     final MapperMethod mapperMethod = cachedMapperMethod(method);  
  31.     //调用mapperMethod.execute 核心的地方就在这个方法里,这个方法对才是真正对SqlSession进行的包装调用  
  32.     return mapperMethod.execute(sqlSession, args);  
  33.   }  
  34.   //缓存处理  
  35.   private MapperMethod cachedMapperMethod(Method method) {  
  36.     MapperMethod mapperMethod = methodCache.get(method);  
  37.     if (mapperMethod == null) {  
  38.       mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());  
  39.       methodCache.put(method, mapperMethod);  
  40.     }  
  41.     return mapperMethod;  
  42.   }  
  43. }  
  44. MapperProxy  
[java]  view plain  copy
  1. package org.apache.ibatis.binding;  
  2. import org.apache.ibatis.annotations.MapKey;  
  3. import org.apache.ibatis.annotations.Param;  
  4. import org.apache.ibatis.mapping.MappedStatement;  
  5. import org.apache.ibatis.mapping.SqlCommandType;  
  6. import org.apache.ibatis.reflection.MetaObject;  
  7. import org.apache.ibatis.session.Configuration;  
  8. import org.apache.ibatis.session.ResultHandler;  
  9. import org.apache.ibatis.session.RowBounds;  
  10. import org.apache.ibatis.session.SqlSession;  
  11. import java.lang.reflect.Array;  
  12. import java.lang.reflect.Method;  
  13. import java.util.*;  
  14. //这个类是整个代理机制的核心类,对Sqlsession当中的操作进行了封装  
  15. public class MapperMethod {  
  16.   //一个内部封 封装了SQL标签的类型 insert update delete select  
  17.   private final SqlCommand command;  
  18.   //一个内部类 封装了方法的参数信息 返回类型信息等  
  19.   private final MethodSignature method;  
  20.   //构造参数  
  21.   public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {  
  22.     this.command = new SqlCommand(config, mapperInterface, method);  
  23.     this.method = new MethodSignature(config, method);  
  24.   }  
  25.   //这个方法是对SqlSession的包装调用  
  26.   public Object execute(SqlSession sqlSession, Object[] args) {  
  27.     //定义返回结果  
  28.     Object result;  
  29.     //如果是INSERT操作  
  30.     if (SqlCommandType.INSERT == command.getType()) {  
  31.       //处理参数  
  32.       Object param = method.convertArgsToSqlCommandParam(args);  
  33.       //调用sqlSession的insert方法  
  34.       result = rowCountResult(sqlSession.insert(command.getName(), param));  
  35.       //如果是UPDATE操作 同上  
  36.     } else if (SqlCommandType.UPDATE == command.getType()) {  
  37.       Object param = method.convertArgsToSqlCommandParam(args);  
  38.       result = rowCountResult(sqlSession.update(command.getName(), param));  
  39.       //如果是DELETE操作 同上  
  40.     } else if (SqlCommandType.DELETE == command.getType()) {  
  41.       Object param = method.convertArgsToSqlCommandParam(args);  
  42.       result = rowCountResult(sqlSession.delete(command.getName(), param));  
  43.       //如果是SELECT操作 那么情况会多一些 但是也都和sqlSession的查询方法一一对应  
  44.     } else if (SqlCommandType.SELECT == command.getType()) {  
  45.       //如果返回void 并且参数有resultHandler  
  46.       //则调用 void select(String statement, Object parameter, ResultHandler handler);方法  
  47.       if (method.returnsVoid() && method.hasResultHandler()) {  
  48.         executeWithResultHandler(sqlSession, args);  
  49.         result = null;  
  50.       //如果返回多行结果这调用 <E> List<E> selectList(String statement, Object parameter);  
  51.       //executeForMany这个方法调用的  
  52.       } else if (method.returnsMany()) {  
  53.         result = executeForMany(sqlSession, args);  
  54.       //如果返回类型是MAP 则调用executeForMap方法  
  55.       } else if (method.returnsMap()) {  
  56.         result = executeForMap(sqlSession, args);  
  57.       } else {  
  58.         //否则就是查询单个对象  
  59.         Object param = method.convertArgsToSqlCommandParam(args);  
  60.         result = sqlSession.selectOne(command.getName(), param);  
  61.       }  
  62.     } else {  
  63.       //如果全都不匹配 说明mapper中定义的方法不对  
  64.       throw new BindingException("Unknown execution method for: " + command.getName());  
  65.     }  
  66.     //如果返回值为空 并且方法返回值类型是基础类型 并且不是VOID 则抛出异常  
  67.     if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {  
  68.       throw new BindingException("Mapper method '" + command.getName()   
  69.           + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");  
  70.     }  
  71.     return result;  
  72.   }  
  73.   private Object rowCountResult(int rowCount) {  
  74.     final Object result;  
  75.     if (method.returnsVoid()) {  
  76.       result = null;  
  77.     } else if (Integer.class.equals(method.getReturnType()) || Integer.TYPE.equals(method.getReturnType())) {  
  78.       result = rowCount;  
  79.     } else if (Long.class.equals(method.getReturnType()) || Long.TYPE.equals(method.getReturnType())) {  
  80.       result = (long) rowCount;  
  81.     } else if (Boolean.class.equals(method.getReturnType()) || Boolean.TYPE.equals(method.getReturnType())) {  
  82.       result = (rowCount > 0);  
  83.     } else {  
  84.       throw new BindingException("Mapper method '" + command.getName() + "' has an unsupported return type: " + method.getReturnType());  
  85.     }  
  86.     return result;  
  87.   }  
  88.   private void executeWithResultHandler(SqlSession sqlSession, Object[] args) {  
  89.     MappedStatement ms = sqlSession.getConfiguration().getMappedStatement(command.getName());  
  90.     if (void.class.equals(ms.getResultMaps().get(0).getType())) {  
  91.       throw new BindingException("method " + command.getName()   
  92.           + " needs either a @ResultMap annotation, a @ResultType annotation,"   
  93.           + " or a resultType attribute in XML so a ResultHandler can be used as a parameter.");  
  94.     }  
  95.     Object param = method.convertArgsToSqlCommandParam(args);  
  96.     if (method.hasRowBounds()) {  
  97.       RowBounds rowBounds = method.extractRowBounds(args);  
  98.       sqlSession.select(command.getName(), param, rowBounds, method.extractResultHandler(args));  
  99.     } else {  
  100.       sqlSession.select(command.getName(), param, method.extractResultHandler(args));  
  101.     }  
  102.   }  
  103.   //返回多行结果 调用sqlSession.selectList方法  
  104.   private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {  
  105.     List<E> result;  
  106.     Object param = method.convertArgsToSqlCommandParam(args);  
  107.     //如果参数含有rowBounds则调用分页的查询  
  108.     if (method.hasRowBounds()) {  
  109.       RowBounds rowBounds = method.extractRowBounds(args);  
  110.       result = sqlSession.<E>selectList(command.getName(), param, rowBounds);  
  111.     } else {  
  112.       //没有分页则调用普通查询  
  113.       result = sqlSession.<E>selectList(command.getName(), param);  
  114.     }  
  115.     // issue #510 Collections & arrays support  
  116.     if (!method.getReturnType().isAssignableFrom(result.getClass())) {  
  117.       if (method.getReturnType().isArray()) {  
  118.         return convertToArray(result);  
  119.       } else {  
  120.         return convertToDeclaredCollection(sqlSession.getConfiguration(), result);  
  121.       }  
  122.     }  
  123.     return result;  
  124.   }  
  125.   private <E> Object convertToDeclaredCollection(Configuration config, List<E> list) {  
  126.     Object collection = config.getObjectFactory().create(method.getReturnType());  
  127.     MetaObject metaObject = config.newMetaObject(collection);  
  128.     metaObject.addAll(list);  
  129.     return collection;  
  130.   }  
  131.   @SuppressWarnings("unchecked")  
  132.   private <E> E[] convertToArray(List<E> list) {  
  133.     E[] array = (E[]) Array.newInstance(method.getReturnType().getComponentType(), list.size());  
  134.     array = list.toArray(array);  
  135.     return array;  
  136.   }  
  137.   private <K, V> Map<K, V> executeForMap(SqlSession sqlSession, Object[] args) {  
  138.     Map<K, V> result;  
  139.     Object param = method.convertArgsToSqlCommandParam(args);  
  140.     if (method.hasRowBounds()) {  
  141.       RowBounds rowBounds = method.extractRowBounds(args);  
  142.       result = sqlSession.<K, V>selectMap(command.getName(), param, method.getMapKey(), rowBounds);  
  143.     } else {  
  144.       result = sqlSession.<K, V>selectMap(command.getName(), param, method.getMapKey());  
  145.     }  
  146.     return result;  
  147.   }  
  148.   public static class ParamMap<V> extends HashMap<String, V> {  
  149.     private static final long serialVersionUID = -2212268410512043556L;  
  150.     @Override  
  151.     public V get(Object key) {  
  152.       if (!super.containsKey(key)) {  
  153.         throw new BindingException("Parameter '" + key + "' not found. Available parameters are " + keySet());  
  154.       }  
  155.       return super.get(key);  
  156.     }  
  157.   }  


[java]  view plain  copy
  1. //一个内部类 封装了具体执行的动作  
  2.   public static class SqlCommand {  
  3.     //xml标签的id  
  4.     private final String name;  
  5.     //insert update delete select的具体类型  
  6.     private final SqlCommandType type;  
  7.     public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) throws BindingException {  
  8.       //拿到全名 比如 org.mybatis.example.BlogMapper.selectBlog  
  9.       String statementName = mapperInterface.getName() + "." + method.getName();  
  10.       MappedStatement ms = null;  
  11.       //获取MappedStatement对象 这个对象封装了XML当中一个标签的所有信息 比如下面  
  12.       //<select id="selectBlog" resultType="Blog">  
  13.       //select * from Blog where id = #{id}  
  14.       //</select>  
  15.       if (configuration.hasStatement(statementName)) {  
  16.         ms = configuration.getMappedStatement(statementName);  
  17.       } else if (!mapperInterface.equals(method.getDeclaringClass().getName())) { // 这里是一个BUG  
  18.         String parentStatementName = method.getDeclaringClass().getName() + "." + method.getName();  
  19.         if (configuration.hasStatement(parentStatementName)) {  
  20.           ms = configuration.getMappedStatement(parentStatementName);  
  21.         }  
  22.       }  
  23.       //为空抛出异常  
  24.       if (ms == null) {  
  25.         throw new BindingException("Invalid bound statement (not found): " + statementName);  
  26.       }  
  27.       name = ms.getId();  
  28.       type = ms.getSqlCommandType();  
  29.       //判断SQL标签类型 未知就抛异常  
  30.       if (type == SqlCommandType.UNKNOWN) {  
  31.         throw new BindingException("Unknown execution method for: " + name);  
  32.       }  
  33.     }  
  34.     public String getName() {  
  35.       return name;  
  36.     }  
  37.     public SqlCommandType getType() {  
  38.       return type;  
  39.     }  
  40.   }  
  41.   //内部类 封装了接口当中方法的 参数类型 返回值类型 等信息  
  42.   public static class MethodSignature {  
  43.     //是否返回多调结果  
  44.     private final boolean returnsMany;  
  45.     //返回值是否是MAP  
  46.     private final boolean returnsMap;  
  47.     //返回值是否是VOID  
  48.     private final boolean returnsVoid;  
  49.     //返回值类型  
  50.     private final Class<?> returnType;  
  51.     //mapKey  
  52.     private final String mapKey;  
  53.     //resultHandler类型参数的位置  
  54.     private final Integer resultHandlerIndex;  
  55.     //rowBound类型参数的位置  
  56.     private final Integer rowBoundsIndex;  
  57.     //用来存放参数信息  
  58.     private final SortedMap<Integer, String> params;  
  59.     //是否存在命名参数  
  60.     private final boolean hasNamedParameters;  
  61.     //在这里对上面的属性进行初始化 就不一一详细说明了 具体实现细节可以看下面的代码。  
  62.     public MethodSignature(Configuration configuration, Method method) throws BindingException {  
  63.       this.returnType = method.getReturnType();  
  64.       this.returnsVoid = void.class.equals(this.returnType);  
  65.       this.returnsMany = (configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray());  
  66.       this.mapKey = getMapKey(method);  
  67.       this.returnsMap = (this.mapKey != null);  
  68.       this.hasNamedParameters = hasNamedParams(method);  
  69.       this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class);  
  70.       this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class);  
  71.       this.params = Collections.unmodifiableSortedMap(getParams(method, this.hasNamedParameters));  
  72.     }  
  73.     public Object convertArgsToSqlCommandParam(Object[] args) {  
  74.       final int paramCount = params.size();  
  75.       if (args == null || paramCount == 0) {  
  76.         return null;  
  77.       } else if (!hasNamedParameters && paramCount == 1) {  
  78.         return args[params.keySet().iterator().next()];  
  79.       } else {  
  80.         final Map<String, Object> param = new ParamMap<Object>();  
  81.         int i = 0;  
  82.         for (Map.Entry<Integer, String> entry : params.entrySet()) {  
  83.           param.put(entry.getValue(), args[entry.getKey()]);  
  84.           // issue #71, add param names as param1, param2...but ensure backward compatibility  
  85.           final String genericParamName = "param" + String.valueOf(i + 1);  
  86.           if (!param.containsKey(genericParamName)) {  
  87.             param.put(genericParamName, args[entry.getKey()]);  
  88.           }  
  89.           i++;  
  90.         }  
  91.         return param;  
  92.       }  
  93.     }  
  94.     public boolean hasRowBounds() {  
  95.       return (rowBoundsIndex != null);  
  96.     }  
  97.     public RowBounds extractRowBounds(Object[] args) {  
  98.       return (hasRowBounds() ? (RowBounds) args[rowBoundsIndex] : null);  
  99.     }  
  100.     public boolean hasResultHandler() {  
  101.       return (resultHandlerIndex != null);  
  102.     }  
  103.     public ResultHandler extractResultHandler(Object[] args) {  
  104.       return (hasResultHandler() ? (ResultHandler) args[resultHandlerIndex] : null);  
  105.     }  
  106.     public String getMapKey() {  
  107.       return mapKey;  
  108.     }  
  109.     public Class<?> getReturnType() {  
  110.       return returnType;  
  111.     }  
  112.     public boolean returnsMany() {  
  113.       return returnsMany;  
  114.     }  
  115.     public boolean returnsMap() {  
  116.       return returnsMap;  
  117.     }  
  118.     public boolean returnsVoid() {  
  119.       return returnsVoid;  
  120.     }  
  121.     private Integer getUniqueParamIndex(Method method, Class<?> paramType) {  
  122.       Integer index = null;  
  123.       final Class<?>[] argTypes = method.getParameterTypes();  
  124.       for (int i = 0; i < argTypes.length; i++) {  
  125.         if (paramType.isAssignableFrom(argTypes[i])) {  
  126.           if (index == null) {  
  127.             index = i;  
  128.           } else {  
  129.             throw new BindingException(method.getName() + " cannot have multiple " + paramType.getSimpleName() + " parameters");  
  130.           }  
  131.         }  
  132.       }  
  133.       return index;  
  134.     }  
  135.     private String getMapKey(Method method) {  
  136.       String mapKey = null;  
  137.       if (Map.class.isAssignableFrom(method.getReturnType())) {  
  138.         final MapKey mapKeyAnnotation = method.getAnnotation(MapKey.class);  
  139.         if (mapKeyAnnotation != null) {  
  140.           mapKey = mapKeyAnnotation.value();  
  141.         }  
  142.       }  
  143.       return mapKey;  
  144.     }  
  145.     private SortedMap<Integer, String> getParams(Method method, boolean hasNamedParameters) {  
  146.       final SortedMap<Integer, String> params = new TreeMap<Integer, String>();  
  147.       final Class<?>[] argTypes = method.getParameterTypes();  
  148.       for (int i = 0; i < argTypes.length; i++) {  
  149.         if (!RowBounds.class.isAssignableFrom(argTypes[i]) && !ResultHandler.class.isAssignableFrom(argTypes[i])) {  
  150.           String paramName = String.valueOf(params.size());  
  151.           if (hasNamedParameters) {  
  152.             paramName = getParamNameFromAnnotation(method, i, paramName);  
  153.           }  
  154.           params.put(i, paramName);  
  155.         }  
  156.       }  
  157.       return params;  
  158.     }  
  159.     private String getParamNameFromAnnotation(Method method, int i, String paramName) {  
  160.       final Object[] paramAnnos = method.getParameterAnnotations()[i];  
  161.       for (Object paramAnno : paramAnnos) {  
  162.         if (paramAnno instanceof Param) {  
  163.           paramName = ((Param) paramAnno).value();  
  164.         }  
  165.       }  
  166.       return paramName;  
  167.     }  
  168.     private boolean hasNamedParams(Method method) {  
  169.       boolean hasNamedParams = false;  
  170.       final Object[][] paramAnnos = method.getParameterAnnotations();  
  171.       for (Object[] paramAnno : paramAnnos) {  
  172.         for (Object aParamAnno : paramAnno) {  
  173.           if (aParamAnno instanceof Param) {  
  174.             hasNamedParams = true;  
  175.             break;  
  176.           }  
  177.         }  
  178.       }  
  179.       return hasNamedParams;  
  180.     }  
  181.   }  
  182. }  
  183. MapperMethod  

通过上面的分析就很容易弄清楚Mybatis是如何利用JDK动态代理的机制生成代理类来对各种Mapper接口进行封装的了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值