<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
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包懶載入功能 內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45