很显然这个方法是更加Class名获取该类的一个实例,而Mapper接口只定义了接口没有实现类,那么猜想可知返回的应该就是更加mapper.xml生成的实例了。具体是如何实现的呢, 先看下这个方法是如何实现的?
DefaultSqlSession实现该方法的代码如下:
1 @Override 2 publicT getMapper(Class type) { 3 return configuration. getMapper(type, this); 4 }
方法很简单,调用了Configuration对象的getMapper方法,那么接下来再看下Configuration里面是如何实现的。代码如下:
1 publicT getMapper(Class type, SqlSession sqlSession) { 2 return mapperRegistry.getMapper(type, sqlSession); 3 }
调用了mapperRegistry的getMapper方法,参数分别是Class对象和sqlSession,再继续看MapperRegistry的实现,代码如下:
@SuppressWarnings("unchecked") publicT getMapper(Class type, SqlSession sqlSession) { // 从konwMappers获取MapperProxyFactory对象 final MapperProxyFactory mapperProxyFactory = (MapperProxyFactory )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); } }
可以看出是根据type从knowMappers集合中获取该mapper的代理工厂类,如何通过该代理工厂新建一个实例。再看下代理工厂是如何创建实例的,代码如下:
@SuppressWarnings("unchecked") protected T newInstance(MapperProxymapperProxy) { //通过代理获取mapper接口的新实例 return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy); } public T newInstance(SqlSession sqlSession) { //创建mapper代理对象,调用newInstance方法 final MapperProxy mapperProxy = new MapperProxy (sqlSession, mapperInterface, methodCache); return newInstance(mapperProxy); }
那么现在我们就知道是如何根据Mapper.class来获取Mapper接口的实例的了,不过,到目前为止貌似还是没有看到和MappedStatement产生联系啊,别急,再往下看通过代理产生的mapper实例执行具体的方法是如何进行的。代码如下:
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //判断执行的方法是否是来自父类Object类的方法,也就是如toString、hashCode等方法 //如果是则直接通过反射执行该方法,如果不是Object的方法则再往下走,如果不加这个判断会发生什么呢? //由于mapper接口除了定义的接口方法还包括继承于Object的方法,如果不加判断则会继续往下走,而下面的执行过程是从mapper.xml寻找对应的实现方法, //由于mapper.xml只实现了mapper中的接口方法,而没有toString和hashCode方法,从而就会导致这些方法无法被实现。 if (Object.class.equals(method.getDeclaringClass())) { try { return method.invoke(this, args); } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } } final MapperMethod mapperMethod = cachedMapperMethod(method); //执行mapperMethod对象的execute方法 return mapperMethod.execute(sqlSession, args); } private MapperMethod cachedMapperMethod(Method method) { //从缓存中根据method对象获取MapperMethod对象 MapperMethod mapperMethod = methodCache.get(method); if (mapperMethod == null) { //如果mapperMethod为空则新建MapperMethod方法 mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()); methodCache.put(method, mapperMethod); } return mapperMethod; }
可以看出代理执行mapper接口的方法会先创建一个MapperMethod对象,然后执行execute方法,代码如下:
public Object execute(SqlSession sqlSession, Object[] args) { Object result; if (SqlCommandType.INSERT == command.getType()) {//如果执行insert命令 Object param = method.convertArgsToSqlCommandParam(args);//构建参数 result = rowCountResult(sqlSession.insert(command.getName(), param));//调用sqlSession的insert方法 } else if (SqlCommandType.UPDATE == command.getType()) {//如果执行update命令 Object param = method.convertArgsToSqlCommandParam(args);//构建参数 result = rowCountResult(sqlSession.update(command.getName(), param));//调用sqlSession的update方法 } else if (SqlCommandType.DELETE == command.getType()) {//如果执行delete命令 Object param = method.convertArgsToSqlCommandParam(args);//构建参数 result = rowCountResult(sqlSession.delete(command.getName(), param));//调用sqlSession的delete方法 } else if (SqlCommandType.SELECT == command.getType()) {//如果执行select命令 if (method.returnsVoid() && method.hasResultHandler()) {//判断接口返回类型,更加返回数据类型执行对应的select语句 executeWithResultHandler(sqlSession, args); result = null; } else if (method.returnsMany()) { result = executeForMany(sqlSession, args); } else if (method.returnsMap()) { result = executeForMap(sqlSession, args); } else if (method.returnsCursor()) { result = executeForCursor(sqlSession, args); } else { Object param = method.convertArgsToSqlCommandParam(args); result = sqlSession.selectOne(command.getName(), param); } } else if (SqlCommandType.FLUSH == command.getType()) { result = sqlSession.flushStatements(); } else { throw new BindingException("Unknown execution method for: " + command.getName()); } if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) { throw new BindingException("Mapper method '" + command.getName() + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ")."); } return result; }
可以看出大致的执行过程就是更加MapperMethod的方法类型,然后构建对应的参数,然后执行sqlSession的方法。到现在还是没有MappedStatement的影子,再看看MapperMethod是被创建的。
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, mapperInterface, method); }
这里涉及到了两个类SqlCommand和MethodSignature,先从SqlCommand看起。
public SqlCommand(Configuration configuration, Class> mapperInterface, Method method) { String statementName = mapperInterface.getName() + "." + method.getName();//接口方法名 MappedStatement ms = null; if (configuration.hasStatement(statementName)) { //从configuration中根据接口名获取MappedStatement对象 ms = configuration.getMappedStatement(statementName); } else if (!mapperInterface.equals(method.getDeclaringClass())) { // issue #35 //如果该方法不是该mapper接口的方法,则从mapper的父类中找寻该接口对应的MappedStatement对象 String parentStatementName = method.getDeclaringClass().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 BindingException("Invalid bound statement (not found): " + statementName); } } else { name = ms.getId();//设置name为MappedStatement的id,而id的值就是xml中对应的sql语句 type = ms.getSqlCommandType();//设置type为MappedStatement的sql类型 if (type == SqlCommandType.UNKNOWN) { throw new BindingException("Unknown execution method for: " + name); } } }
这里终于是看到了MappedStatement的身影,根据mapper的Class对象和method对象从Configuration对象中获取指定的MappedStatement对象,然后根据MappedStatement对象的值初始化SqlCommand对象的属性。而MethodSignature则是sql语句的签名,主要作用就是对sql参数与返回结果类型的判断。
总结:
1、sqlSession调用configuration对象的getMapper方法,configuration调用mapperRegistry的getMapper方法
2、mapperRegistry根据mapper获取对应的Mapper代理工厂
3、通过mapper代理工厂创建mapper的代理
4、执行mapper方法时,通过代理调用,创建该mapper方法的MapperMethod对象
5、MapperMethod对象的创建是通过从configuration对象中获取指定的MappedStatement对象来获取具体的sql语句以及参数和返回结果类型
6、调用sqlSession对应的insert、update、delete和select方法执行mapper的方法
到目前为止知道了mapper接口和mapper.xml是如何进行关联的了,也知道mapper接口是如何获取实例的了,也知道了mapper方法最终会调用SqlSession的方法,那么SqlSession又是如何具体去执行每个Sql方法的呢?下一篇继续分析......