Mybatis懶加載的實現
因為通過javassist和cglib代理實現的,所以說到底最主要的就是JavasisstProxyFactory類中的invoke方法和里面的load方法。
其實讀一讀,里面的邏輯就是跟配置中定義的規(guī)則一樣的
因為github上的mybatis中文版中這部分注釋比較少,所以從網上尋找博客,截取了代碼注釋片段記錄下。
JavasisstProxyFactory
public class JavassistProxyFactory implements org.apache.ibatis.executor.loader.ProxyFactory {
/**
* 接口實現
* @param target 目標結果對象
* @param lazyLoader 延遲加載對象
* @param configuration 配置
* @param objectFactory 對象工廠
* @param constructorArgTypes 構造參數類型
* @param constructorArgs 構造參數值
* @return
*/
@Override
public Object createProxy(Object target, ResultLoaderMap lazyLoader, Configuration configuration, ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
return EnhancedResultObjectProxyImpl.createProxy(target, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
}
//省略...
/**
* 代理對象實現,核心邏輯執(zhí)行
*/
private static class EnhancedResultObjectProxyImpl implements MethodHandler {
/**
* 創(chuàng)建代理對象
* @param type
* @param callback
* @param constructorArgTypes
* @param constructorArgs
* @return
*/
static Object crateProxy(Class<?> type, MethodHandler callback, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
ProxyFactory enhancer = new ProxyFactory();
enhancer.setSuperclass(type);
try {
//通過獲取對象方法,判斷是否存在該方法
type.getDeclaredMethod(WRITE_REPLACE_METHOD);
// ObjectOutputStream will call writeReplace of objects returned by writeReplace
if (log.isDebugEnabled()) {
log.debug(WRITE_REPLACE_METHOD + " method was found on bean " + type + ", make sure it returns this");
}
} catch (NoSuchMethodException e) {
//沒找到該方法,實現接口
enhancer.setInterfaces(new Class[]{WriteReplaceInterface.class});
} catch (SecurityException e) {
// nothing to do here
}
Object enhanced;
Class<?>[] typesArray = constructorArgTypes.toArray(new Class[constructorArgTypes.size()]);
Object[] valuesArray = constructorArgs.toArray(new Object[constructorArgs.size()]);
try {
//創(chuàng)建新的代理對象
enhanced = enhancer.create(typesArray, valuesArray);
} catch (Exception e) {
throw new ExecutorException("Error creating lazy proxy. Cause: " + e, e);
}
//設置代理執(zhí)行器
((Proxy) enhanced).setHandler(callback);
return enhanced;
}
/**
* 代理對象執(zhí)行
* @param enhanced 原對象
* @param method 原對象方法
* @param methodProxy 代理方法
* @param args 方法參數
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object enhanced, Method method, Method methodProxy, Object[] args) throws Throwable {
final String methodName = method.getName();
try {
synchronized (lazyLoader) {
if (WRITE_REPLACE_METHOD.equals(methodName)) {
//忽略暫未找到具體作用
Object original;
if (constructorArgTypes.isEmpty()) {
original = objectFactory.create(type);
} else {
original = objectFactory.create(type, constructorArgTypes, constructorArgs);
}
PropertyCopier.copyBeanProperties(type, enhanced, original);
if (lazyLoader.size() > 0) {
return new JavassistSerialStateHolder(original, lazyLoader.getProperties(), objectFactory, constructorArgTypes, constructorArgs);
} else {
return original;
}
} else {
//延遲加載數量大于0
if (lazyLoader.size() > 0 && !FINALIZE_METHOD.equals(methodName)) {
//aggressive 一次加載性所有需要要延遲加載屬性或者包含觸發(fā)延遲加載方法
if (aggressive || lazyLoadTriggerMethods.contains(methodName)) {
log.debug("==> laze lod trigger method:" + methodName + ",proxy method:" + methodProxy.getName() + " class:" + enhanced.getClass());
//一次全部加載
lazyLoader.loadAll();
} else if (PropertyNamer.isSetter(methodName)) {
//判斷是否為set方法,set方法不需要延遲加載
final String property = PropertyNamer.methodToProperty(methodName);
lazyLoader.remove(property);
} else if (PropertyNamer.isGetter(methodName)) {
final String property = PropertyNamer.methodToProperty(methodName);
if (lazyLoader.hasLoader(property)) {
//延遲加載單個屬性
lazyLoader.load(property);
log.debug("load one :" + methodName);
}
}
}
}
}
return methodProxy.invoke(enhanced, args);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
}
load方法
/**
* 執(zhí)行懶加載查詢,獲取數據并且set到userObject中返回
* @param userObject
* @throws SQLException
*/
public void load(final Object userObject) throws SQLException {
//合法性校驗
if (this.metaResultObject == null || this.resultLoader == null) {
if (this.mappedParameter == null) {
throw new ExecutorException("Property [" + this.property + "] cannot be loaded because "
+ "required parameter of mapped statement ["
+ this.mappedStatement + "] is not serializable.");
}
//獲取mappedstatement并且校驗
final Configuration config = this.getConfiguration();
final MappedStatement ms = config.getMappedStatement(this.mappedStatement);
if (ms == null) {
throw new ExecutorException("Cannot lazy load property [" + this.property
+ "] of deserialized object [" + userObject.getClass()
+ "] because configuration does not contain statement ["
+ this.mappedStatement + "]");
}
//使用userObject構建metaobject,并且重新構建resultloader對象
this.metaResultObject = config.newMetaObject(userObject);
this.resultLoader = new ResultLoader(config, new ClosedExecutor(), ms, this.mappedParameter,
metaResultObject.getSetterType(this.property), null, null);
}
/* We are using a new executor because we may be (and likely are) on a new thread
* and executors aren't thread safe. (Is this sufficient?)
*
* A better approach would be making executors thread safe. */
if (this.serializationCheck == null) {
final ResultLoader old = this.resultLoader;
this.resultLoader = new ResultLoader(old.configuration, new ClosedExecutor(), old.mappedStatement,
old.parameterObject, old.targetType, old.cacheKey, old.boundSql);
}
//獲取數據庫查詢結果并且set到結果對象返回
this.metaResultObject.setValue(property, this.resultLoader.loadResult());
}
參考地址:
https://www.cnblogs.com/qixidi/p/10251126.html
https://blog.csdn.net/mingtian625/article/details/47358003
到此這篇關于Mybatis懶加載的實現的文章就介紹到這了,更多相關Mybatis 懶加載內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
詳解spring cloud如何使用spring-test進行單元測試
這篇文章主要介紹了spring cloud如何使用spring-test進行單元測試,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2019-11-11
logback FixedWindowRollingPolicy固定窗口算法重命名文件滾動策略
這篇文章主要介紹了FixedWindowRollingPolicy根據logback 固定窗口算法重命名文件滾動策略源碼解讀,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-11-11

