mybatis源码解析六(代理模式再分析)

mybatis源码解析六(代理模式再分析)
前面几期大概一起看了下maybatis的源码,这一期,我们通过设计模式来分析下mybatis的,但是在分析之前,我们再来屡一下mybatis的执行流程,做一个整体的讲解,
我们都知道,当我们写了mapper接口后,通过mapper接口myabtis就可以执行相应的查询,那马这里mybatis到底是怎么做到的那,前面已经分析关于一次了,但是比较笼统,
今天我们再一起分析下,我们都知道,在我们写完mapper接口后,我们在每个mapper接口上添加@Mapper注解或者在启动类上添加@MapperScan注解后,基本上剩下的工作就不用我们
在做了,mybatis会根据我们的xml文件中配置的sql语句,为我们返回结果,那到底mybatis是怎么通过接口就去生成实现类,来完成上述操作的那,我们自己也没有写实现类啊,
接下来,我们从源码中寻找答案
我写了个计较简单的查询查询
@Test
@Transactional
public void test5(){
final RyxAccount ryxAccountByPrimaryKey = ryxAccountService.getRyxAccountByPrimaryKey(1);
System.out.println(ryxAccountByPrimaryKey);
final RyxAccount ryxAccountByPrimaryKey1 = ryxAccountService.getRyxAccountByPrimaryKey(1);
System.out.println(ryxAccountByPrimaryKey1);

}

mapper接口
public interface RyxAccountMapper extends BaseMapper{

    /**
     * 根据primaryKey更新
     */
     public Integer updateByPrimaryKey(@Param("bean") T t, @Param("id") Integer id);


    /**
     * 根据primaryKey删除
     */
     public Integer deleteByPrimaryKey(@Param("id") Integer id);


    /**
     * 根据primaryKey获取对象
     */
     public T selectByPrimaryKey(@Param("id") Integer id);


    List> selectListPage(@Param("bindex") int bindex, @Param("num") int num);

    public List selectTest(@Param("id")int id);

    Cursor batchReader(@Param("id")int id);

    Cursor selectCursorTest(@Param("beanP")RyxAccountCusorParam param);
}

接下来,我们通过断点的方式,一探究竟
首先,当我们启动项目的时候,因为配置了@Mapperscan的原因,mybaits回去我们配置的扫描包下扫描接口(加载配置文件,获取连接,这些,就不做具体分析了),
当扫描到接口后,会进入到mybatis的MapperRegistry

看看他的appMapper方法
public void addMapper(Class type) {
//分析是不是接口
if (type.isInterface()) {
//如果接口已经存在,则抛出异常
if (hasMapper(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
//不存在,则将扫描到的mapper接口添加到knowerMapper中,knowerMapper是一个map,key为具体的mapper类,value为mapperProxyFactory
knownMappers.put(type, new MapperProxyFactory(type));

    //创建mapper注解解析器对象
    MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
    //执行paser方法
    parser.parse();
    loadCompleted = true;
  } finally {
    if (!loadCompleted) {
      knownMappers.remove(type);
    }
  }
}

}

public void parse() {
String resource = type.toString();
if (!configuration.isResourceLoaded(resource)) {
//检查资源名称是否被加载过
loadXmlResource();
//设置当前的命名空间
configuration.addLoadedResource(resource);
assistant.setCurrentNamespace(type.getName());
//解析是否配置了二级缓存注解@CacheNameSpace(这里解释下,和xml文件中配置了缓存是一个意思)
parseCache();
//解析二级缓存引用
parseCacheRef();
//获取接口中的方法
Method[] methods = type.getMethods();
//遍历此方法
for (Method method : methods) {
try {
// issue #237
//检查是否是桥接方法(主要目的是泛型的兼容问题)
if (!method.isBridge()) {
//解析语句
parseStatement(method);
}
} catch (IncompleteElementException e) {
configuration.addIncompleteMethod(new MethodResolver(this, method));
}
}
}
parsePendingMethods();
}

void parseStatement(Method method) {
//获取mapper接口方法中的参数类型
Class parameterTypeClass = getParameterType(method);
//获取语言驱动类
LanguageDriver languageDriver = getLanguageDriver(method);
//从注解获取sql源信息类(就是说我们在方法上配置了注解之后,这里就是获取方法上的注解的方法)
SqlSource sqlSource = getSqlSourceFromAnnotations(method, parameterTypeClass, languageDriver);
if (sqlSource != null) {
Options options = method.getAnnotation(Options.class);
final String mappedStatementId = type.getName() + "." + method.getName();
Integer fetchSize = null;
Integer timeout = null;
StatementType statementType = StatementType.PREPARED;
ResultSetType resultSetType = ResultSetType.FORWARD_ONLY;
SqlCommandType sqlCommandType = getSqlCommandType(method);
boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
boolean flushCache = !isSelect;
boolean useCache = isSelect;

     KeyGenerator keyGenerator;
     String keyProperty = "id";
     String keyColumn = null;
     if (SqlCommandType.INSERT.equals(sqlCommandType) || SqlCommandType.UPDATE.equals(sqlCommandType)) {
       // first check for SelectKey annotation - that overrides everything else
       SelectKey selectKey = method.getAnnotation(SelectKey.class);
       if (selectKey != null) {
         keyGenerator = handleSelectKeyAnnotation(selectKey, mappedStatementId, getParameterType(method), languageDriver);
         keyProperty = selectKey.keyProperty();
       } else if (options == null) {
         keyGenerator = configuration.isUseGeneratedKeys() ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
       } else {
         keyGenerator = options.useGeneratedKeys() ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
         keyProperty = options.keyProperty();
         keyColumn = options.keyColumn();
       }
     } else {
       keyGenerator = NoKeyGenerator.INSTANCE;
     }

     if (options != null) {
       if (FlushCachePolicy.TRUE.equals(options.flushCache())) {
         flushCache = true;
       } else if (FlushCachePolicy.FALSE.equals(options.flushCache())) {
         flushCache = false;
       }
       useCache = options.useCache();
       fetchSize = options.fetchSize() > -1 || options.fetchSize() == Integer.MIN_VALUE ? options.fetchSize() : null; //issue #348
       timeout = options.timeout() > -1 ? options.timeout() : null;
       statementType = options.statementType();
       resultSetType = options.resultSetType();
     }

     String resultMapId = null;
     ResultMap resultMapAnnotation = method.getAnnotation(ResultMap.class);
     if (resultMapAnnotation != null) {
       String[] resultMaps = resultMapAnnotation.value();
       StringBuilder sb = new StringBuilder();
       for (String resultMap : resultMaps) {
         if (sb.length() > 0) {
           sb.append(",");
         }
         sb.append(resultMap);
       }
       resultMapId = sb.toString();
     } else if (isSelect) {
       resultMapId = parseResultMap(method);
     }

     assistant.addMappedStatement(
         mappedStatementId,
         sqlSource,
         statementType,
         sqlCommandType,
         fetchSize,
         timeout,
         // ParameterMapID
         null,
         parameterTypeClass,
         resultMapId,
         getReturnType(method),
         resultSetType,
         flushCache,
         useCache,
         // TODO gcode issue #577
         false,
         keyGenerator,
         keyProperty,
         keyColumn,
         // DatabaseID
         null,
         languageDriver,
         // ResultSets
         options != null ? nullOrEmpty(options.resultSets()) : null);
   }
 }

 等这些做完之后那,我们继续往下看,加载sqlSesson我们先不做分析,直接看MappperProxy,还记得我们之前的分析嘛,myBatis将扫描完的接口添加到knowMappers中

其中key是具体得接口类,value为mapperProxyFactory,再获取到sqlSession后,最终会在MapperFactoryBean中调用getMapper
MapperFactoryBean,由于整合spring框架,这里不再调用mybatis的DefaultSqlSession,而是调用了SqlSessionTemplete

@Override
public T getObject() throws Exception {
//调用SqlSessionTemplete的getMapper方法
return getSqlSession().getMapper(this.mapperInterface);
}

public T getMapper(Class type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}

//最终回到了MapperRegistry中的getMapper方法
public T getMapper(Class type, SqlSession sqlSession) {
//从knownMappers通过key获取value,mapperProxyFactory
final MapperProxyFactory mapperProxyFactory = (MapperProxyFactory) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
//通过jdk动态代理的方式生成代理类
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}

public T newInstance(SqlSession sqlSession) {
final MapperProxy mapperProxy = new MapperProxy(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}

protected T newInstance(MapperProxy mapperProxy) {
//代用jdk动态代理
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}

最终看到了,我们实现代理的类,mapperProxy,我们看看这个类,实现了InvocationHandler接口,最终通过mapperMethod.execute方法去数据库查询数据并返回结果,
这路下来,是不是感觉真的是踏破铁鞋无觅处,柳暗花明又一村啊,真的是太难了,
public class MapperProxy implements InvocationHandler, Serializable {

private static final long serialVersionUID = -6424540398559729838L;
private final SqlSession sqlSession;
private final Class mapperInterface;
private final Map methodCache;

public MapperProxy(SqlSession sqlSession, Class mapperInterface, Map 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);
}

}
可能这样不太好看,我们仿照jdk的动态代理自己先写一个代理类,来模仿下.看看大神门的代码精髓
我们先自定义一个接口.
public interface RyxMapper {

String save(String str);

}

自定义一个mapperProxy
public class MapperProxy implements InvocationHandler {

private Class proxyInterface;

public MapperProxy(Class proxyInterface) {
    this.proxyInterface = proxyInterface;
}

@SuppressWarnings("unchecked")
public T getProxy() {
        return (T) Proxy.newProxyInstance(proxyInterface.getClassLoader(), new Class[]{proxyInterface}, this);
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    System.out.println("我要开始执行了....查询数据库操作了");
    System.out.println("查库操作已完成,返回结果啦");
    return "数据库结果集:"+"123";
}

}

写一个测试类执行下:
public static void main(String[] args) {

    RyxMapper mapper = (RyxMapper)new MapperProxy(RyxMapper.class).getProxy();
    final String ok = mapper.save("ok");
    System.out.println(ok);
}

最终的执行结果肯定是不言而喻的,没有问题,最终会打印出结果,真的是书上得来终觉浅,实践是检验真理的唯一途径啊.
ok,咱们接着往下分析,现在通过代理,我们拿到了要执行的sql语句和参数
接下来,回去调用
mapperMethod.execute(sqlSession, args);这个方法
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
switch (command.getType()) {
case INSERT: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
break;
}
case UPDATE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
break;
}
case DELETE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
break;
}
case SELECT:
if (method.returnsVoid() && method.hasResultHandler()) {
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);
}
break;
case FLUSH:
result = sqlSession.flushStatements();
break;
default:
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;
}

这段源码就不在赘述了,咱们直接看一个最简单的例子吧,
sqlSession.selectOne(command.getName(), param);
最终会走到DefautSqlSession中执行查询操作,这里,我们看到了,一个很警醒的注释,DefaultSqlSession是非线程安全的,下一章我们分析下这个问题
/**

  • The default implementation for {@link SqlSession}.
  • Note that this class is not Thread-Safe.
  • @author Clinton Begin
    */
    public class DefaultSqlSession implements SqlSession {
    以上就是今天的内容,Thanks

你可能感兴趣的:(mybatis源码解析六(代理模式再分析))