MyBatis源码分析
Mybatis一般按照分析来说,可以按照三步曲进行
1、xml解析对应的配置文件
2、将配置文件通过XMLConfigBuilder解析生成Configuration对象。
3、SqlSessionFactoryBuilder通过builder方法将Configuration对象存入到里面。
4、configuration委托给MapperRegistry:运用动态代理生成对应的Dao对象.然后拿到配置文件中对应的id的sql语句,利用SqlSession执行相应的操作。
一般面试会问mybatis的接口是怎么实现的,其原理就是用采用jdk的动态代理来实现的,只是它的接口并没有实现类,动态代理的invoke方法中得到当前的method方法去构造MapperMethod对象,看具体代码:
public <T> T getMapper(Class<T> type, SqlSession sqlSession) { return this.mapperRegistry.getMapper(type, sqlSession); }
通过mapperregistry获取对应的对象,进一步看看getMapper的代码:
public <T> T getMapper(Class<T> type, SqlSession sqlSession) { MapperProxyFactory mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type); if(mapperProxyFactory == null) { throw new BindingException("Type " + type + " is not known to the MapperRegistry."); } else { try { return mapperProxyFactory.newInstance(sqlSession); } catch (Exception var5) { throw new BindingException("Error getting mapper instance. Cause: " + var5, var5); } } }
可以看到,动态代理开始了,通过代理来产生对应的对象,但是mybatis的代理是接口的,并没有实现,不要忘记哦,来看看具体的代码:
protected T newInstance(MapperProxy<T> mapperProxy) { return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy); //动态代理模式生成对应的类 } public T newInstance(SqlSession sqlSession) { MapperProxy mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache); return this.newInstance(mapperProxy); }
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { if(Object.class.equals(method.getDeclaringClass())) { return method.invoke(this, args); } if(this.isDefaultMethod(method)) { return this.invokeDefaultMethod(proxy, method, args); } } catch (Throwable var5) { throw ExceptionUtil.unwrapThrowable(var5); } //这里去拿到对应的sql语句 MapperMethod mapperMethod = this.cachedMapperMethod(method); return mapperMethod.execute(this.sqlSession, args); }
在来看看MapperMethod中的execute的方法:
public Object execute(SqlSession sqlSession, Object[] args) { Object param; Object result; switch(null.$SwitchMap$org$apache$ibatis$mapping$SqlCommandType[this.command.getType().ordinal()]) { case 1: param = this.method.convertArgsToSqlCommandParam(args); result = this.rowCountResult(sqlSession.insert(this.command.getName(), param)); break; case 2: param = this.method.convertArgsToSqlCommandParam(args); result = this.rowCountResult(sqlSession.update(this.command.getName(), param)); break; case 3: param = this.method.convertArgsToSqlCommandParam(args); result = this.rowCountResult(sqlSession.delete(this.command.getName(), param)); break; case 4: if(this.method.returnsVoid() && this.method.hasResultHandler()) { this.executeWithResultHandler(sqlSession, args); result = null; } else if(this.method.returnsMany()) { result = this.executeForMany(sqlSession, args); } else if(this.method.returnsMap()) { result = this.executeForMap(sqlSession, args); } else if(this.method.returnsCursor()) { result = this.executeForCursor(sqlSession, args); } else { param = this.method.convertArgsToSqlCommandParam(args); result = sqlSession.selectOne(this.command.getName(), param); } break; case 5: result = sqlSession.flushStatements(); break; default: throw new BindingException("Unknown execution method for: " + this.command.getName()); } if(result == null && this.method.getReturnType().isPrimitive() && !this.method.returnsVoid()) { throw new BindingException("Mapper method \'" + this.command.getName() + " attempted to return null from a method with a primitive return type (" + this.method.getReturnType() + ")."); } else { return result; } }
可以看到,最终还是通过SqlSession来执行增删查改。