mybatis源碼解讀之executor包懶加載功能
ProxyFactory
是創建代理類的工廠接口,其中的setProperties
方法用來對工廠進行屬性設置,但是mybatis內置的兩個實現類都沒有實現該接口,所以不支持屬性設置。createProxy
方法用來創建一個代理對象
public interface ProxyFactory { // 設置工廠屬性 default void setProperties(Properties properties) { } // 創建代理對象 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方法的處理方式是:
如果設置瞭激進懶加載或者被調用的是觸發全局加載的方法,則直接加載所有未加載的屬性。
如果被調用的是屬性寫方法,則將該方法從懶加載列表中刪除,因為此時數據庫中的數據已經不是最新的,沒有必要再去加載,然後進行屬性的寫入操作。
如果被調用的是讀方法,則該屬性尚未被懶加載的情況下,則加載該屬性,如果該屬性已經被懶加載過,則直接讀取該屬性。
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; } }
到此這篇關於mybatis
源碼解讀之executor
包懶加載功能 的文章就介紹到這瞭,更多相關executor包懶加載功能 內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- 一文瞭解mybatis的延遲加載
- 基於mybatis-plus timestamp返回為null問題的排除
- Java Collections的emptyList、EMPTY_LIST詳解與使用說明
- java返回集合為null還是空集合及空集合的三種寫法小結
- Spring IOC中的Bean對象用法