Mybatis原始碼解讀:executor包(懶載入功能)
技術標籤:mybaits原始碼mybatis原始碼
歡迎關注本人公眾號:
1.懶載入功能的使用
要使用懶載入,需要在mybatis的配置檔案中啟用該功能。
<setting>
<settingname="lazyLoadingEnable"value="true"/>
</setting>
mybatis的懶載入的實現由executor包的loader子包支援。
2.懶載入功能的實現
2.1代理工廠
ProxyFactory是建立代理類的工廠介面,其中的setProperties方法用來對工廠進行屬性設定,但是mybatis內建的兩個實現類都沒有實現該介面,所以不支援屬性設定。createProxy方法用來建立一個代理物件
public interface ProxyFactory {
// 設定工廠屬性
defaultvoidsetProperties(Propertiesproperties){
}
// 建立代理物件
Object createProxy(Object target, ResultLoaderMap lazyLoader, Configuration configuration, ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs);
}
ProxyFactory介面有2個實現類,CglibProxyFactory和JavassistProxyFactor類。這兩個實現類整體結構高度一致,內部類、方法設定都一樣,只是實現原理不同。CglibProxyFactory基於cglib實現,JavassistProxyFactor基於javassist實現。
接下來以CglibProxyFactory類為原始碼進行分析:
CglibProxyFactory類中提供了兩個建立代理物件的方法。其中createProxy方法重寫了一個普通的代理物件,createDeserializationProxy方法用來建立一個反序列化的代理物件。
public class CglibProxyFactory implements ProxyFactory {
private static final String FINALIZE_METHOD = "finalize";
private static final String WRITE_REPLACE_METHOD = "writeReplace";
public CglibProxyFactory() {
try {
Resources.classForName("net.sf.cglib.proxy.Enhancer");
} catch (Throwable e) {
throw new IllegalStateException("Cannot enable lazy loading because CGLIB is not available. Add CGLIB to your classpath.", e);
}
}
// 建立一個代理
@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);
}
// 建立一個反序列化的代理
public Object createDeserializationProxy(Object target, Map<String, ResultLoaderMap.LoadPair> unloadedProperties, ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
return EnhancedDeserializationProxyImpl.createProxy(target, unloadedProperties, objectFactory, constructorArgTypes, constructorArgs);
}
private static class EnhancedResultObjectProxyImpl implements MethodInterceptor {
// 被代理類
private final Class<?> type;
// 要懶載入的屬性Map
private final ResultLoaderMap lazyLoader;
// 是否是激進懶載入
private final boolean aggressive;
// 能夠觸發懶載入的方法名“equals”, “clone”, “hashCode”, “toString”。這四個方法名在Configuration中被初始化。
private final Set<String> lazyLoadTriggerMethods;
// 物件工廠
private final ObjectFactory objectFactory;
// 被代理類建構函式的引數型別列表
private final List<Class<?>> constructorArgTypes;
// 被代理類建構函式的引數列表
private final List<Object> constructorArgs;
private EnhancedResultObjectProxyImpl(Class<?> type, ResultLoaderMap lazyLoader, Configuration configuration, ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
this.type = type;
this.lazyLoader = lazyLoader;
this.aggressive = configuration.isAggressiveLazyLoading();
this.lazyLoadTriggerMethods = configuration.getLazyLoadTriggerMethods();
this.objectFactory = objectFactory;
this.constructorArgTypes = constructorArgTypes;
this.constructorArgs = constructorArgs;
}
public static Object createProxy(Object target, ResultLoaderMap lazyLoader, Configuration configuration, ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
final Class<?> type = target.getClass();
EnhancedResultObjectProxyImpl callback = new EnhancedResultObjectProxyImpl(type, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
Object enhanced = crateProxy(type, callback, constructorArgTypes, constructorArgs);
PropertyCopier.copyBeanProperties(type, target, enhanced);
return enhanced;
}
/**
* 代理類的攔截方法
* @param enhanced 代理物件本身
* @param method 被呼叫的方法
* @param args 每呼叫的方法的引數
* @param methodProxy 用來呼叫父類的代理
* @return 方法返回值
* @throws Throwable
*/
@Override
public Object intercept(Object enhanced, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
// 取出被代理類中此次被呼叫的方法的名稱
final String methodName = method.getName();
try {
synchronized (lazyLoader) { // 防止屬性的併發載入
if (WRITE_REPLACE_METHOD.equals(methodName)) { // 被呼叫的是writeReplace方法
// 建立一個原始物件
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) { // 存在懶載入屬性
// 則此時返回的資訊要更多,不僅僅是原物件,還有相關的懶載入的設定等資訊。因此使用CglibSerialStateHolder進行一次封裝
return new CglibSerialStateHolder(original, lazyLoader.getProperties(), objectFactory, constructorArgTypes, constructorArgs);
} else {
// 沒有未懶載入的屬性了,那直接返回原物件進行序列化
return original;
}
} else {
if (lazyLoader.size() > 0 && !FINALIZE_METHOD.equals(methodName)) { // 存在懶載入屬性且被呼叫的不是finalize方法
if (aggressive || lazyLoadTriggerMethods.contains(methodName)) { // 設定了激進懶載入或者被呼叫的方法是能夠觸發全域性懶載入的方法
// 完成所有屬性的懶載入
lazyLoader.loadAll();
} else if (PropertyNamer.isSetter(methodName)) { // 呼叫了屬性寫方法
// 則先清除該屬性的懶載入設定。該屬性不需要被懶載入了
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);
}
}
}
}
}
// 觸發被代理類的相應方法。能夠進行到這裡的是除去writeReplace方法外的方法,例如讀寫方法、toString方法等
return methodProxy.invokeSuper(enhanced, args);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
}
}
代理類最核心的方法是intercept方法,當被代理物件的其他方法被呼叫時,intercept方法的處理方式是:
-
如果設定了激進懶載入或者被呼叫的是觸發全域性載入的方法,則直接載入所有未載入的屬性。
-
如果被呼叫的是屬性寫方法,則將該方法從懶載入列表中刪除,因為此時資料庫中的資料已經不是最新的,沒有必要再去載入,然後進行屬性的寫入操作。
-
如果被呼叫的是讀方法,則該屬性尚未被懶載入的情況下,則載入該屬性,如果該屬性已經被懶載入過,則直接讀取該屬性。
3.ResultLoaderMap類
被代理物件可能會有多個屬性可以被懶載入,這些尚未完成載入的屬性是在ResultLoaderMap類的例項中儲存的。ResultLoaderMap類主要就是一個map類,該類key為屬性名的大寫,value為LoadPair物件。LoadPair類是ResultLoaderMap類的內部類,它能實現對應屬性的懶載入功能。
public static class LoadPair implements Serializable {
private static final long serialVersionUID = 20130412;
// 用來根據反射得到資料庫連線的方法名
private static final String FACTORY_METHOD = "getConfiguration";
// 判斷是否經過了序列化的標誌位,因為該屬性被設定了transient,經過一次序列化和反序列化後會變為null
private final transient Object serializationCheck = new Object();
// 輸出結果物件的封裝
private transient MetaObject metaResultObject;
// 用以載入未載入屬性的載入器
private transient ResultLoader resultLoader;
// 日誌記錄器
private transient Log log;
// 用來獲取資料庫連線的工廠
private Class<?> configurationFactory;
// 未載入的屬性的屬性名
private String property;
// 能夠載入未載入屬性的SQL的編號
private String mappedStatement;
// 能夠載入未載入屬性的SQL的引數
private Serializable mappedParameter;
private LoadPair(final String property, MetaObject metaResultObject, ResultLoader resultLoader) {
this.property = property;
this.metaResultObject = metaResultObject;
this.resultLoader = resultLoader;
if (metaResultObject != null && metaResultObject.getOriginalObject() instanceof Serializable) {
final Object mappedStatementParameter = resultLoader.parameterObject;
if (mappedStatementParameter instanceof Serializable) {
this.mappedStatement = resultLoader.mappedStatement.getId();
this.mappedParameter = (Serializable) mappedStatementParameter;
this.configurationFactory = resultLoader.configuration.getConfigurationFactory();
} else {
Log log = this.getLogger();
if (log.isDebugEnabled()) {
log.debug("Property [" + this.property + "] of ["
+ metaResultObject.getOriginalObject().getClass() + "] cannot be loaded "
+ "after deserialization. Make sure it's loaded before serializing "
+ "forenamed object.");
}
}
}
}
public void load() throws SQLException {
if (this.metaResultObject == null) {
throw new IllegalArgumentException("metaResultObject is null");
}
if (this.resultLoader == null) {
throw new IllegalArgumentException("resultLoader is null");
}
this.load(null);
}
/**
* 進行載入操作
* @param userObject 需要被懶載入的物件(只有當this.metaResultObject == null || this.resultLoader == null才生效,否則會採用屬性metaResultObject對應的物件)
* @throws SQLException
*/
public void load(final Object userObject) throws SQLException {
if (this.metaResultObject == null || this.resultLoader == null) { // 輸出結果物件的封裝不存在或者輸出結果載入器不存在
// 判斷用以載入屬性的對應的SQL語句存在
if (this.mappedParameter == null) {
throw new ExecutorException("Property [" + this.property + "] cannot be loaded because "
+ "required parameter of mapped statement ["
+ this.mappedStatement + "] is not serializable.");
}
final Configuration config = this.getConfiguration();
// 取出用來載入結果的SQL語句
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 + "]");
}
// 建立結果物件的包裝
this.metaResultObject = config.newMetaObject(userObject);
// 建立結果載入器
this.resultLoader = new ResultLoader(config, new ClosedExecutor(), ms, this.mappedParameter,
metaResultObject.getSetterType(this.property), null, null);
}
// 只要經歷過持久化,則可能在別的執行緒中了。為這次惰性載入建立的新執行緒ResultLoader
if (this.serializationCheck == null) {
// 取出原來的ResultLoader中的必要資訊,然後建立一個新的
// 這是因為load函式可能在不同的時間多次執行(第一次載入屬性A,又過了好久載入屬性B)。
// 而該物件的各種屬性是跟隨物件的,載入屬性B時還保留著載入屬性A時的狀態,即ResultLoader是載入屬性A時設定的
// 則此時ResultLoader中的Executor在ResultLoader中被替換成了一個能執行的Executor,而不是ClosedExecutor
// 能執行的Executor的狀態可能不是close,這將導致它被複用,從而引發多執行緒問題
// 是不是被兩次執行的一個關鍵點就是有沒有經過序列化,因為執行完後會被序列化並持久化
final ResultLoader old = this.resultLoader;
this.resultLoader = new ResultLoader(old.configuration, new ClosedExecutor(), old.mappedStatement,
old.parameterObject, old.targetType, old.cacheKey, old.boundSql);
}
this.metaResultObject.setValue(property, this.resultLoader.loadResult());
}
private Configuration getConfiguration() {
if (this.configurationFactory == null) {
throw new ExecutorException("Cannot get Configuration as configuration factory was not set.");
}
Object configurationObject;
try {
final Method factoryMethod = this.configurationFactory.getDeclaredMethod(FACTORY_METHOD);
if (!Modifier.isStatic(factoryMethod.getModifiers())) {
throw new ExecutorException("Cannot get Configuration as factory method ["
+ this.configurationFactory + "]#["
+ FACTORY_METHOD + "] is not static.");
}
if (!factoryMethod.isAccessible()) {
configurationObject = AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
try {
factoryMethod.setAccessible(true);
return factoryMethod.invoke(null);
} finally {
factoryMethod.setAccessible(false);
}
});
} else {
configurationObject = factoryMethod.invoke(null);
}
} catch (final ExecutorException ex) {
throw ex;
} catch (final NoSuchMethodException ex) {
throw new ExecutorException("Cannot get Configuration as factory class ["
+ this.configurationFactory + "] is missing factory method of name ["
+ FACTORY_METHOD + "].", ex);
} catch (final PrivilegedActionException ex) {
throw new ExecutorException("Cannot get Configuration as factory method ["
+ this.configurationFactory + "]#["
+ FACTORY_METHOD + "] threw an exception.", ex.getCause());
} catch (final Exception ex) {
throw new ExecutorException("Cannot get Configuration as factory method ["
+ this.configurationFactory + "]#["
+ FACTORY_METHOD + "] threw an exception.", ex);
}
if (!(configurationObject instanceof Configuration)) {
throw new ExecutorException("Cannot get Configuration as factory method ["
+ this.configurationFactory + "]#["
+ FACTORY_METHOD + "] didn't return [" + Configuration.class + "] but ["
+ (configurationObject == null ? "null" : configurationObject.getClass()) + "].");
}
return Configuration.class.cast(configurationObject);
}
private Log getLogger() {
if (this.log == null) {
this.log = LogFactory.getLog(this.getClass());
}
return this.log;
}
}
4.ResultLoader類
ResultLoader類是一個結果載入器類,它負責完成資料的載入工作。因為懶載入只涉及查詢,而不需要支援增,刪,改的工作,所以它只有一個查詢方法selectList來進行資料的查詢。
publicclassResultLoader{
public Object loadResult() throws SQLException {
// 查詢結果列表
List<Object> list = selectList();
// 把結果列表轉化為指定物件
resultObject = resultExtractor.extractObjectFromList(list, targetType);
return resultObject;
}
private <E> List<E> selectList() throws SQLException {
// 初始化ResultLoader時傳入的執行器
Executor localExecutor = executor;
if (Thread.currentThread().getId() != this.creatorThreadId || localExecutor.isClosed()) {
// 執行器關閉,或者執行器屬於其他執行緒,則建立新的執行器
localExecutor = newExecutor();
}
try {
// 查詢結果
return localExecutor.query(mappedStatement, parameterObject, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER, cacheKey, boundSql);
} finally {
if (localExecutor != executor) {
localExecutor.close(false);
}
}
}
}
5.懶載入功能對序列化和反序列化的支援
如果要對查詢結果進行序列化,實際上是對代理物件即EnhancedResultObjecctProxyImpl物件進行序列化,因為該類已經替換了被代理物件。為了保證懶載入操作支援序列化和反序列化,則必須保證在序列化時將被代理物件和代理物件的所有資訊全都儲存。所以在CglibProxyFactory中建立代理物件時,無論是建立EnhancedResultObjectProxyImpl類還是EnhancedDeserializationProxyImpl類,都會在它們的構造方法中呼叫createProxy方法。
// 建立一個代理
@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);
}
// 建立一個反序列化的代理
public Object createDeserializationProxy(Object target, Map<String, ResultLoaderMap.LoadPair> unloadedProperties, ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
return EnhancedDeserializationProxyImpl.createProxy(target, unloadedProperties, objectFactory, constructorArgTypes, constructorArgs);
}
/**
* 建立代理物件
* @param type 被代理物件型別
* @param callback 回撥物件
* @param constructorArgTypes 構造方法引數型別列表
* @param constructorArgs 構造方法引數型別
* @return 代理物件
*/
static Object crateProxy(Class<?> type, Callback callback, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
Enhancer enhancer = new Enhancer();
enhancer.setCallback(callback);
// 建立的是代理物件是原物件的子類
enhancer.setSuperclass(type);
try {
// 獲取類中的writeReplace方法
type.getDeclaredMethod(WRITE_REPLACE_METHOD);
if (LogHolder.log.isDebugEnabled()) {
LogHolder.log.debug(WRITE_REPLACE_METHOD + " method was found on bean " + type + ", make sure it returns this");
}
} catch (NoSuchMethodException e) {
// 如果沒找到writeReplace方法,則設定代理類繼承WriteReplaceInterface介面,該介面中有writeReplace方法
enhancer.setInterfaces(new Class[]{WriteReplaceInterface.class});
} catch (SecurityException e) {
// 什麼都不做
}
Object enhanced;
if (constructorArgTypes.isEmpty()) {
enhanced = enhancer.create();
} else {
Class<?>[] typesArray = constructorArgTypes.toArray(new Class[constructorArgTypes.size()]);
Object[] valuesArray = constructorArgs.toArray(new Object[constructorArgs.size()]);
enhanced = enhancer.create(typesArray, valuesArray);
}
return enhanced;
}
createProxy方法中一個重要的操作是校驗被代理類中是否含有writeReplace方法。如果被代理類沒有該方法,則會讓代理類繼承WriteReplaceInterface獲得一個writeReplace方法,因為該方法會在物件序列化前被呼叫,起到偷樑換柱的作用。