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

mybatis源碼解析六(代理模式再分析)
前面幾期大概一起看了下maybatis的源碼,這一期,我們通過(guò)設(shè)計(jì)模式來(lái)分析下mybatis的,但是在分析之前,我們?cè)賮?lái)屢一下mybatis的執(zhí)行流程,做一個(gè)整體的講解,
我們都知道,當(dāng)我們寫了mapper接口后,通過(guò)mapper接口myabtis就可以執(zhí)行相應(yīng)的查詢,那馬這里mybatis到底是怎么做到的那,前面已經(jīng)分析關(guān)于一次了,但是比較籠統(tǒng),
今天我們?cè)僖黄鸱治鱿?我們都知道,在我們寫完mapper接口后,我們?cè)诿總€(gè)mapper接口上添加@Mapper注解或者在啟動(dòng)類上添加@MapperScan注解后,基本上剩下的工作就不用我們
在做了,mybatis會(huì)根據(jù)我們的xml文件中配置的sql語(yǔ)句,為我們返回結(jié)果,那到底mybatis是怎么通過(guò)接口就去生成實(shí)現(xiàn)類,來(lái)完成上述操作的那,我們自己也沒(méi)有寫實(shí)現(xiàn)類啊,
接下來(lái),我們從源碼中尋找答案
我寫了個(gè)計(jì)較簡(jiǎn)單的查詢查詢
@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<T,P> extends BaseMapper<T,P>{

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


    /**
     * 根據(jù)primaryKey刪除
     */
     public Integer deleteByPrimaryKey(@Param("id") Integer id);


    /**
     * 根據(jù)primaryKey獲取對(duì)象
     */
     public T selectByPrimaryKey(@Param("id") Integer id);


    List<Map<String,Object>> selectListPage(@Param("bindex") int bindex, @Param("num") int num);

    public List<RyxAccount> selectTest(@Param("id")int id);

    Cursor<RyxAccount> batchReader(@Param("id")int id);

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

接下來(lái),我們通過(guò)斷點(diǎn)的方式,一探究竟
首先,當(dāng)我們啟動(dòng)項(xiàng)目的時(shí)候,因?yàn)榕渲昧薂Mapperscan的原因,mybaits回去我們配置的掃描包下掃描接口(加載配置文件,獲取連接,這些,就不做具體分析了),
當(dāng)掃描到接口后,會(huì)進(jìn)入到mybatis的MapperRegistry

看看他的appMapper方法
public <T> void addMapper(Class<T> type) {
//分析是不是接口
if (type.isInterface()) {
//如果接口已經(jīng)存在,則拋出異常
if (hasMapper(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
//不存在,則將掃描到的mapper接口添加到knowerMapper中,knowerMapper是一個(gè)map,key為具體的mapper類,value為mapperProxyFactory
knownMappers.put(type, new MapperProxyFactory<T>(type));

    //創(chuàng)建mapper注解解析器對(duì)象
    MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
    //執(zhí)行paser方法
    parser.parse();
    loadCompleted = true;
  } finally {
    if (!loadCompleted) {
      knownMappers.remove(type);
    }
  }
}

}

public void parse() {
String resource = type.toString();
if (!configuration.isResourceLoaded(resource)) {
//檢查資源名稱是否被加載過(guò)
loadXmlResource();
//設(shè)置當(dāng)前的命名空間
configuration.addLoadedResource(resource);
assistant.setCurrentNamespace(type.getName());
//解析是否配置了二級(jí)緩存注解@CacheNameSpace(這里解釋下,和xml文件中配置了緩存是一個(gè)意思)
parseCache();
//解析二級(jí)緩存引用
parseCacheRef();
//獲取接口中的方法
Method[] methods = type.getMethods();
//遍歷此方法
for (Method method : methods) {
try {
// issue #237
//檢查是否是橋接方法(主要目的是泛型的兼容問(wèn)題)
if (!method.isBridge()) {
//解析語(yǔ)句
parseStatement(method);
}
} catch (IncompleteElementException e) {
configuration.addIncompleteMethod(new MethodResolver(this, method));
}
}
}
parsePendingMethods();
}

void parseStatement(Method method) {
//獲取mapper接口方法中的參數(shù)類型
Class<?> parameterTypeClass = getParameterType(method);
//獲取語(yǔ)言驅(qū)動(dòng)類
LanguageDriver languageDriver = getLanguageDriver(method);
//從注解獲取sql源信息類(就是說(shuō)我們?cè)诜椒ㄉ吓渲昧俗⒔庵?這里就是獲取方法上的注解的方法)
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);
   }
 }

 等這些做完之后那,我們繼續(xù)往下看,加載sqlSesson我們先不做分析,直接看MappperProxy,還記得我們之前的分析嘛,myBatis將掃描完的接口添加到knowMappers中

其中key是具體得接口類,value為mapperProxyFactory,再獲取到sqlSession后,最終會(huì)在MapperFactoryBean中調(diào)用getMapper
MapperFactoryBean,由于整合spring框架,這里不再調(diào)用mybatis的DefaultSqlSession,而是調(diào)用了SqlSessionTemplete

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

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

//最終回到了MapperRegistry中的getMapper方法
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
//從knownMappers通過(guò)key獲取value,mapperProxyFactory
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
//通過(guò)jdk動(dòng)態(tài)代理的方式生成代理類
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}

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

protected T newInstance(MapperProxy<T> mapperProxy) {
//代用jdk動(dòng)態(tài)代理
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}

最終看到了,我們實(shí)現(xiàn)代理的類,mapperProxy,我們看看這個(gè)類,實(shí)現(xiàn)了InvocationHandler接口,最終通過(guò)mapperMethod.execute方法去數(shù)據(jù)庫(kù)查詢數(shù)據(jù)并返回結(jié)果,
這路下來(lái),是不是感覺(jué)真的是踏破鐵鞋無(wú)覓處,柳暗花明又一村啊,真的是太難了,
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);
}

}
可能這樣不太好看,我們仿照jdk的動(dòng)態(tài)代理自己先寫一個(gè)代理類,來(lái)模仿下.看看大神門的代碼精髓
我們先自定義一個(gè)接口.
public interface RyxMapper {

String save(String str);

}

自定義一個(gè)mapperProxy
public class MapperProxy<T> implements InvocationHandler {

private Class<T> proxyInterface;

public MapperProxy(Class<T> 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("我要開始執(zhí)行了....查詢數(shù)據(jù)庫(kù)操作了");
    System.out.println("查庫(kù)操作已完成,返回結(jié)果啦");
    return "數(shù)據(jù)庫(kù)結(jié)果集:"+"123";
}

}

寫一個(gè)測(cè)試類執(zhí)行下:
public static void main(String[] args) {

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

最終的執(zhí)行結(jié)果肯定是不言而喻的,沒(méi)有問(wèn)題,最終會(huì)打印出結(jié)果,真的是書上得來(lái)終覺(jué)淺,實(shí)踐是檢驗(yàn)真理的唯一途徑啊.
ok,咱們接著往下分析,現(xiàn)在通過(guò)代理,我們拿到了要執(zhí)行的sql語(yǔ)句和參數(shù)
接下來(lái),回去調(diào)用
mapperMethod.execute(sqlSession, args);這個(gè)方法
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;
}

這段源碼就不在贅述了,咱們直接看一個(gè)最簡(jiǎn)單的例子吧,
sqlSession.selectOne(command.getName(), param);
最終會(huì)走到DefautSqlSession中執(zhí)行查詢操作,這里,我們看到了,一個(gè)很警醒的注釋,DefaultSqlSession是非線程安全的,下一章我們分析下這個(gè)問(wèn)題
/**

  • The default implementation for {@link SqlSession}.
  • Note that this class is not Thread-Safe.
  • @author Clinton Begin
    */
    public class DefaultSqlSession implements SqlSession {
    以上就是今天的內(nèi)容,Thanks
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容