当定义好一个Mapper接口(UserDao)
里,我们并不需要去实现这个类,但sqlSession.getMapper()
最终会返回一个实现该接口的对象。这个对象是Mybatis
利用jdk
的动态代理实现的。这里将介绍这个代理对象的生成过程及其方法的实现过程。
Mapper 接口获取与注册添加   一般mybatis
项目实例化对应Mapper
接口:
1 2 3 4 5 6 7 8 9 SqlSession sqlSession = MySqlSessionFactory.openSession();try { StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class); studentMapper.insertStudent(student); sqlSession.commit(); } finally { sqlSession.close(); }
**sqlSession.getMapper(StudentMapper.class);**底层调用链为:DefultSqlSession.getMapper()
-> Configuration.getMapper()
-> MapperRegistry.getMapper()
:
1 2 3 4 5 6 7 8 9 public <T> T getMapper (Class<T> type) { return this .configuration.getMapper(type, this ); } public <T> T getMapper (Class<T> type, SqlSession sqlSession) { return mapperRegistry.getMapper(type, sqlSession); }
Mybatis
初始化时会在读取mapper.xml文件后会调用org.apache.ibatis.builder.xml.XMLMapperBuilder
类的bindMapperForNamespace()
方法,绑定到命名空间:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 private void bindMapperForNamespace () { String namespace = builderAssistant.getCurrentNamespace(); if (namespace != null ) { Class<?> boundType = null ; try { boundType = Resources.classForName(namespace); } catch (ClassNotFoundException e) { } if (boundType != null ) { if (!configuration.hasMapper(boundType)) { configuration.addLoadedResource("namespace:" + namespace); configuration.addMapper(boundType); } } } } public <T> void addMapper (Class<T> type) { this .mapperRegistry.addMapper(type); }
发现添加和获取Mapper实例都使用到了同一个类MapperRegistry
,在Configuration
中的声明如下:
1 protected final MapperRegistry mapperRegistry = new MapperRegistry (this );
MapperRegistry MapperRegistry
源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 public class MapperRegistry { private final Configuration config; private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap <Class<?>, MapperProxyFactory<?>>(); public MapperRegistry (Configuration config) { this .config = config; } @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 { return mapperProxyFactory.newInstance(sqlSession); } catch (Exception e) { throw new BindingException ("Error getting mapper instance. Cause: " + e, e); } } public <T> boolean hasMapper (Class<T> type) { return knownMappers.containsKey(type); } 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 { knownMappers.put(type, new MapperProxyFactory <T>(type)); MapperAnnotationBuilder parser = new MapperAnnotationBuilder (config, type); parser.parse(); loadCompleted = true ; } finally { if (!loadCompleted) { knownMappers.remove(type); } } } } public Collection<Class<?>> getMappers() { return Collections.unmodifiableCollection(knownMappers.keySet()); } public void addMappers (String packageName, Class<?> superType) { ResolverUtil<Class<?>> resolverUtil = new ResolverUtil <Class<?>>(); resolverUtil.find(new ResolverUtil .IsA(superType), packageName); Set<Class<? extends Class <?>>> mapperSet = resolverUtil.getClasses(); for (Class<?> mapperClass : mapperSet) { addMapper(mapperClass); } } public void addMappers (String packageName) { addMappers(packageName, Object.class); } }
MapperRegistry
有多个addMapper
方法,这里主要解析public <T> void addMapper(Class<T> type)
和public <T> T getMapper(Class<T> type, SqlSession sqlSession)
addMapper 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 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 { knownMappers.put(type, new MapperProxyFactory <T>(type)); MapperAnnotationBuilder parser = new MapperAnnotationBuilder (config, type); parser.parse(); loadCompleted = true ; } finally { if (!loadCompleted) { knownMappers.remove(type); } } } }
主要分为以下几步:
验证要添加的映射器的类型是否是接口,如果不是接口则结束添加,如果是接口则执行下一步
验证注册器集合中是否已存在该注册器(即重复注册验证),如果已存在则抛出绑定异常,否则执行下一步
定义一个boolean
值,默认为false
执行HashMap
集合的put
方法,将该映射器注册到注册器中:以该接口类型为键,以接口类型为参数调用MapperProxyFactory
的构造器创建的映射器代理工厂为值
然后对使用注解方式实现的映射器进行注册(一般不使用)
设置第三步的boolean
值为true
,表示注册完成
在finally
语句块中对注册失败的类型进行清除
步骤十分简单,重点在MapperProxyFactory
代理工厂类的实现。
getMapper addMapper
在MapperRegistry
注册了对应接口,getMapper
就可以去获取对应接口实例:
1 2 3 4 5 6 7 8 9 10 11 12 13 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 { return mapperProxyFactory.newInstance(sqlSession); } catch (Exception e) { throw new BindingException ("Error getting mapper instance. Cause: " + e, e); } }
MapperProxyFactory 和 MapperProxy `MapperRegistry`中在`map`中存储了接口及对应代理工厂类:`knownMappers.put(type, new MapperProxyFactory<T>(type));`;之后又利用该代理工厂去动态代理对应接口实例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 public class MapperProxyFactory <T> { private final Class<T> mapperInterface; private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap <Method, MapperMethod>(); public MapperProxyFactory (Class<T> mapperInterface) { this .mapperInterface = mapperInterface; } public Class<T> getMapperInterface () { return mapperInterface; } public Map<Method, MapperMethod> getMethodCache () { return methodCache; } @SuppressWarnings("unchecked") protected T newInstance (MapperProxy<T> mapperProxy) { return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class [] { mapperInterface }, mapperProxy); } public T newInstance (SqlSession sqlSession) { final MapperProxy<T> mapperProxy = new MapperProxy <T>(sqlSession, mapperInterface, methodCache); return newInstance(mapperProxy); } }
MapperProxyFactory
利用动态代理及工厂模式生成Mapper
接口的实例,具体代理类则是MapperProxy
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 public class MapperProxy <T> implements InvocationHandler , Serializable { private static final long serialVersionUID = -6424540398559729838L ; private final SqlSession sqlSession; private final Class<T> mapperInterface; 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 = methodCache; } @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); } final MapperMethod mapperMethod = cachedMapperMethod(method); return mapperMethod.execute(sqlSession, args); } private MapperMethod cachedMapperMethod (Method method) { MapperMethod mapperMethod = methodCache.get(method); if (mapperMethod == null ) { mapperMethod = new MapperMethod (mapperInterface, method, sqlSession.getConfiguration()); methodCache.put(method, mapperMethod); } return mapperMethod; } @UsesJava7 private Object invokeDefaultMethod (Object proxy, Method method, Object[] args) throws Throwable { final Constructor<MethodHandles.Lookup> constructor = MethodHandles.Lookup.class .getDeclaredConstructor(Class.class, int .class); if (!constructor.isAccessible()) { constructor.setAccessible(true ); } final Class<?> declaringClass = method.getDeclaringClass(); return constructor .newInstance(declaringClass, MethodHandles.Lookup.PRIVATE | MethodHandles.Lookup.PROTECTED | MethodHandles.Lookup.PACKAGE | MethodHandles.Lookup.PUBLIC) .unreflectSpecial(method, declaringClass).bindTo(proxy).invokeWithArguments(args); } private boolean isDefaultMethod (Method method) { return (method.getModifiers() & (Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC)) == Modifier.PUBLIC && method.getDeclaringClass().isInterface(); } }
可以看到,所有mapper
接口的方法经过代理后,最后都是执行了MapperMethod
中的execute(sqlSession, args)
方法。而MapperMethod
也是整个代理机制中最重要的部分,它对对Sqlsession
当中的操作进行了封装。
MapperMethod与MappedStatement实现方法与标签的绑定 初始化 mybatis
初始化时会对mybatis-config.xml
文件进行解析,在这一步中,会解析<mappers>
标签中指向的mapper.xml
文件。之后会根据xml
的namespace
和各个标签属性(select、delete、update、insert),及标签id创建MapperStatement
对象列表,并存储至Configuration
对象中。
方法调用链表如下:
SqlSessionFactoryBuilder.builder()
-> XMLConfigBuilder.parse()
-> XMLConfigBuilder.parseConfiguration()
-> XMLConfigBuilder.mapperElement()
-> XMLMapperBuilder.parse()
-> XMLMapperBuilder.configurationElement()
-> XMLMapperBuilder.buildStatementFromContext()
-> XMLMapperBuilder.buildStatementFromContext()
-> XMLStatementBuilder.parseStatementNode()
-> MapperBuilderAssistant.addMappedStatement()
-> Configuration.addMappedStatement()
在调用MapperBuilderAssistant.addMappedStatement
时,会将mapper.xml
文件的namespace
及其中标签的id
创建存入MappedStatement
对象的id中。然后configuration.addMappedStatement
会以MappedStatement
的id为key,MappedStatement
对象为value存入map中。
调用 之前已经讲到,所有Mapper
接口方法都由MapperProxy
动态代理。在invoke方法中会创建MapperMethod
对象:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 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); } final MapperMethod mapperMethod = cachedMapperMethod(method); return mapperMethod.execute(sqlSession, args); } private MapperMethod cachedMapperMethod (Method method) { MapperMethod mapperMethod = methodCache.get(method); if (mapperMethod == null ) { mapperMethod = new MapperMethod (mapperInterface, method, sqlSession.getConfiguration()); methodCache.put(method, mapperMethod); } return mapperMethod; }
初始化MapperMethod
对象时,会初始化其内部类SqlCommand
:
1 2 3 4 public MapperMethod (Class<?> mapperInterface, Method method, Configuration config) { this .command = new SqlCommand (config, mapperInterface, method); this .method = new MethodSignature (config, mapperInterface, method); }
初始化SqlCommand
对象时,会根据mapper
接口类名及方法名去获取对应MappedStatement
对象,实现一一对应。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public SqlCommand (Configuration configuration, Class<?> mapperInterface, Method method) { final String methodName = method.getName(); final Class<?> declaringClass = method.getDeclaringClass(); MappedStatement ms = resolveMappedStatement(mapperInterface, methodName, declaringClass, configuration); if (ms == null ) { if (method.getAnnotation(Flush.class) != null ) { name = null ; type = SqlCommandType.FLUSH; } else { throw new BindingException ("Invalid bound statement (not found): " + mapperInterface.getName() + "." + methodName); } } else { name = ms.getId(); type = ms.getSqlCommandType(); if (type == SqlCommandType.UNKNOWN) { throw new BindingException ("Unknown execution method for: " + name); } } }