mybatis源碼解讀之executor包懶加載功能?
ProxyFactory
是創(chuàng)建代理類的工廠接口,其中的setProperties
方法用來對(duì)工廠進(jìn)行屬性設(shè)置,但是mybatis內(nèi)置的兩個(gè)實(shí)現(xiàn)類都沒有實(shí)現(xiàn)該接口,所以不支持屬性設(shè)置。createProxy
方法用來創(chuàng)建一個(gè)代理對(duì)象
public interface ProxyFactory { ? // 設(shè)置工廠屬性 ? default void setProperties(Properties properties) { ? } ? // 創(chuàng)建代理對(duì)象 ? Object createProxy(Object target, ResultLoaderMap lazyLoader, Configuration configuration, ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs); }
ProxyFactory
接口有2個(gè)實(shí)現(xiàn)類,CglibProxyFactory
和JavassistProxyFactor
類。這兩個(gè)實(shí)現(xiàn)類整體結(jié)構(gòu)高度一致,內(nèi)部類、方法設(shè)置都一樣,只是實(shí)現(xiàn)原理不同。CglibProxyFactory
基于cglib實(shí)現(xiàn),JavassistProxyFactor
基于javassist實(shí)現(xiàn)。
接下來以CglibProxyFactory類為源碼進(jìn)行分析:
CglibProxyFactory
類中提供了兩個(gè)創(chuàng)建代理對(duì)象的方法。其中createProxy
方法重寫了一個(gè)普通的代理對(duì)象,createDeserializationProxy方法用來創(chuàng)建一個(gè)反序列化的代理對(duì)象。
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); ? ? } ? } ? // 創(chuàng)建一個(gè)代理 ? @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); ? } ? // 創(chuàng)建一個(gè)反序列化的代理 ? 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; ? ? // 是否是激進(jìn)懶加載 ? ? private final boolean aggressive; ? ? // 能夠觸發(fā)懶加載的方法名“equals”, “clone”, “hashCode”, “toString”。這四個(gè)方法名在Configuration中被初始化。 ? ? private final Set<String> lazyLoadTriggerMethods; ? ? // 對(duì)象工廠 ? ? private final ObjectFactory objectFactory; ? ? // 被代理類構(gòu)造函數(shù)的參數(shù)類型列表 ? ? private final List<Class<?>> constructorArgTypes; ? ? // 被代理類構(gòu)造函數(shù)的參數(shù)列表 ? ? 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 代理對(duì)象本身 ? ? ?* @param method 被調(diào)用的方法 ? ? ?* @param args 每調(diào)用的方法的參數(shù) ? ? ?* @param methodProxy 用來調(diào)用父類的代理 ? ? ?* @return 方法返回值 ? ? ?* @throws Throwable ? ? ?*/ ? ? @Override ? ? public Object intercept(Object enhanced, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { ? ? ? // 取出被代理類中此次被調(diào)用的方法的名稱 ? ? ? final String methodName = method.getName(); ? ? ? try { ? ? ? ? synchronized (lazyLoader) { // 防止屬性的并發(fā)加載 ? ? ? ? ? if (WRITE_REPLACE_METHOD.equals(methodName)) { // 被調(diào)用的是writeReplace方法 ? ? ? ? ? ? // 創(chuàng)建一個(gè)原始對(duì)象 ? ? ? ? ? ? Object original; ? ? ? ? ? ? if (constructorArgTypes.isEmpty()) { ? ? ? ? ? ? ? original = objectFactory.create(type); ? ? ? ? ? ? } else { ? ? ? ? ? ? ? original = objectFactory.create(type, constructorArgTypes, constructorArgs); ? ? ? ? ? ? } ? ? ? ? ? ? // 將被代理對(duì)象的屬性拷貝進(jìn)入新創(chuàng)建的對(duì)象 ? ? ? ? ? ? PropertyCopier.copyBeanProperties(type, enhanced, original); ? ? ? ? ? ? if (lazyLoader.size() > 0) { // 存在懶加載屬性 ? ? ? ? ? ? ? // 則此時(shí)返回的信息要更多,不僅僅是原對(duì)象,還有相關(guān)的懶加載的設(shè)置等信息。因此使用CglibSerialStateHolder進(jìn)行一次封裝 ? ? ? ? ? ? ? return new CglibSerialStateHolder(original, lazyLoader.getProperties(), objectFactory, constructorArgTypes, constructorArgs); ? ? ? ? ? ? } else { ? ? ? ? ? ? ? // 沒有未懶加載的屬性了,那直接返回原對(duì)象進(jìn)行序列化 ? ? ? ? ? ? ? return original; ? ? ? ? ? ? } ? ? ? ? ? } else { ? ? ? ? ? ? if (lazyLoader.size() > 0 && !FINALIZE_METHOD.equals(methodName)) { // 存在懶加載屬性且被調(diào)用的不是finalize方法 ? ? ? ? ? ? ? if (aggressive || lazyLoadTriggerMethods.contains(methodName)) { // 設(shè)置了激進(jìn)懶加載或者被調(diào)用的方法是能夠觸發(fā)全局懶加載的方法 ? ? ? ? ? ? ? ? // 完成所有屬性的懶加載 ? ? ? ? ? ? ? ? lazyLoader.loadAll(); ? ? ? ? ? ? ? } else if (PropertyNamer.isSetter(methodName)) { // 調(diào)用了屬性寫方法 ? ? ? ? ? ? ? ? // 則先清除該屬性的懶加載設(shè)置。該屬性不需要被懶加載了 ? ? ? ? ? ? ? ? final String property = PropertyNamer.methodToProperty(methodName); ? ? ? ? ? ? ? ? lazyLoader.remove(property); ? ? ? ? ? ? ? } else if (PropertyNamer.isGetter(methodName)) { // 調(diào)用了屬性讀方法 ? ? ? ? ? ? ? ? final String property = PropertyNamer.methodToProperty(methodName); ? ? ? ? ? ? ? ? // 如果該屬性是尚未加載的懶加載屬性,則進(jìn)行懶加載 ? ? ? ? ? ? ? ? if (lazyLoader.hasLoader(property)) { ? ? ? ? ? ? ? ? ? lazyLoader.load(property); ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? } ? ? ? ? ? ? } ? ? ? ? ? } ? ? ? ? } ? ? ? ? // 觸發(fā)被代理類的相應(yīng)方法。能夠進(jìn)行到這里的是除去writeReplace方法外的方法,例如讀寫方法、toString方法等 ? ? ? ? return methodProxy.invokeSuper(enhanced, args); ? ? ? } catch (Throwable t) { ? ? ? ? throw ExceptionUtil.unwrapThrowable(t); ? ? ? } ? ? } ? } ? }
代理類最核心的方法是intercept
方法,當(dāng)被代理對(duì)象的其他方法被調(diào)用時(shí),intercept方法的處理方式是:
如果設(shè)置了激進(jìn)懶加載或者被調(diào)用的是觸發(fā)全局加載的方法,則直接加載所有未加載的屬性。
如果被調(diào)用的是屬性寫方法,則將該方法從懶加載列表中刪除,因?yàn)榇藭r(shí)數(shù)據(jù)庫中的數(shù)據(jù)已經(jīng)不是最新的,沒有必要再去加載,然后進(jìn)行屬性的寫入操作。
如果被調(diào)用的是讀方法,則該屬性尚未被懶加載的情況下,則加載該屬性,如果該屬性已經(jīng)被懶加載過,則直接讀取該屬性。
ResultLoaderMap類:
被代理對(duì)象可能會(huì)有多個(gè)屬性可以被懶加載,這些尚未完成加載的屬性是在ResultLoaderMap
類的實(shí)例中存儲(chǔ)的。ResultLoaderMap
類主要就是一個(gè)map類,該類key為屬性名的大寫,value
為L(zhǎng)oadPair對(duì)象。LoadPair類是ResultLoaderMap類的內(nèi)部類,它能實(shí)現(xiàn)對(duì)應(yīng)屬性的懶加載功能。
public static class LoadPair implements Serializable { ? ? private static final long serialVersionUID = 20130412; ?? ? ? // 用來根據(jù)反射得到數(shù)據(jù)庫連接的方法名 ? ? private static final String FACTORY_METHOD = "getConfiguration"; ? ?? ? ? // 判斷是否經(jīng)過了序列化的標(biāo)志位,因?yàn)樵搶傩员辉O(shè)置了transient,經(jīng)過一次序列化和反序列化后會(huì)變?yōu)閚ull ? ? private final transient Object serializationCheck = new Object(); ? ? ? ? // 輸出結(jié)果對(duì)象的封裝 ? ? private transient MetaObject metaResultObject; ? ? ? ? // 用以加載未加載屬性的加載器 ? ? private transient ResultLoader resultLoader; ?? ? ? // 日志記錄器 ? ? private transient Log log; ? ?? ? ? // 用來獲取數(shù)據(jù)庫連接的工廠 ? ? private Class<?> configurationFactory; ? ?? ? ? // 未加載的屬性的屬性名 ? ? private String property; ? ? ? ? // 能夠加載未加載屬性的SQL的編號(hào) ? ? private String mappedStatement; ?? ? ? // 能夠加載未加載屬性的SQL的參數(shù) ? ? 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); ? ? } ? ? /** ? ? ?* 進(jìn)行加載操作 ? ? ?* @param userObject 需要被懶加載的對(duì)象(只有當(dāng)this.metaResultObject == null || this.resultLoader == null才生效,否則會(huì)采用屬性metaResultObject對(duì)應(yīng)的對(duì)象) ? ? ?* @throws SQLException ? ? ?*/ ? ? public void load(final Object userObject) throws SQLException { ? ? ? if (this.metaResultObject == null || this.resultLoader == null) { // 輸出結(jié)果對(duì)象的封裝不存在或者輸出結(jié)果加載器不存在 ? ? ? ? // 判斷用以加載屬性的對(duì)應(yīng)的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(); ? ? ? ? // 取出用來加載結(jié)果的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 + "]"); ? ? ? ? } ? ? ? ? // 創(chuàng)建結(jié)果對(duì)象的包裝 ? ? ? ? this.metaResultObject = config.newMetaObject(userObject); ? ? ? ? // 創(chuàng)建結(jié)果加載器 ? ? ? ? this.resultLoader = new ResultLoader(config, new ClosedExecutor(), ms, this.mappedParameter, ? ? ? ? ? ? ? ? metaResultObject.getSetterType(this.property), null, null); ? ? ? } ? ? ? // 只要經(jīng)歷過持久化,則可能在別的線程中了。為這次惰性加載創(chuàng)建的新線程ResultLoader ? ? ? if (this.serializationCheck == null) { ? ? ? ? // 取出原來的ResultLoader中的必要信息,然后創(chuàng)建一個(gè)新的 ? ? ? ? // 這是因?yàn)閘oad函數(shù)可能在不同的時(shí)間多次執(zhí)行(第一次加載屬性A,又過了好久加載屬性B)。 ? ? ? ? // 而該對(duì)象的各種屬性是跟隨對(duì)象的,加載屬性B時(shí)還保留著加載屬性A時(shí)的狀態(tài),即ResultLoader是加載屬性A時(shí)設(shè)置的 ? ? ? ? // 則此時(shí)ResultLoader中的Executor在ResultLoader中被替換成了一個(gè)能運(yùn)行的Executor,而不是ClosedExecutor ? ? ? ? // 能運(yùn)行的Executor的狀態(tài)可能不是close,這將導(dǎo)致它被復(fù)用,從而引發(fā)多線程問題 ? ? ? ? // 是不是被兩次執(zhí)行的一個(gè)關(guān)鍵點(diǎn)就是有沒有經(jīng)過序列化,因?yàn)閳?zhí)行完后會(huì)被序列化并持久化 ? ? ? ? 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; ? ? } ? }
到此這篇關(guān)于mybatis
源碼解讀之executor
包懶加載功能 的文章就介紹到這了,更多相關(guān)executor包懶加載功能 內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Springboot MultipartFile文件上傳與下載的實(shí)現(xiàn)示例
在Spring Boot項(xiàng)目中,可以使用MultipartFile類來處理文件上傳和下載操作,本文就詳細(xì)介紹了如何使用,具有一定的參考價(jià)值,感興趣的可以了解一下2023-08-08SpringBoot的服務(wù)注冊(cè)與發(fā)現(xiàn)示例
本篇文章主要介紹了SpringBoot的服務(wù)注冊(cè)與發(fā)現(xiàn)示例,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-05-05Eclipse如何導(dǎo)入Maven項(xiàng)目詳解(新手初學(xué))
這篇文章主要介紹了Eclipse如何導(dǎo)入Maven項(xiàng)目詳解(新手初學(xué)),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-12-12SpringBoot動(dòng)態(tài)定時(shí)任務(wù)實(shí)現(xiàn)完整版
最近有幸要開發(fā)個(gè)動(dòng)態(tài)定時(shí)任務(wù),這里簡(jiǎn)單再梳理一下,下面這篇文章主要給大家介紹了關(guān)于SpringBoot動(dòng)態(tài)定時(shí)任務(wù)實(shí)現(xiàn)的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-02-02