mybatis源碼解讀之executor包懶加載功能?
ProxyFactory是創(chuàng)建代理類的工廠接口,其中的setProperties方法用來對工廠進行屬性設置,但是mybatis內置的兩個實現類都沒有實現該接口,所以不支持屬性設置。createProxy方法用來創(chuàng)建一個代理對象
public interface ProxyFactory {
? // 設置工廠屬性
? default void setProperties(Properties properties) {
? }
? // 創(chuàng)建代理對象
? 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類中提供了兩個創(chuàng)建代理對象的方法。其中createProxy方法重寫了一個普通的代理對象,createDeserializationProxy方法用來創(chuàng)建一個反序列化的代理對象。
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)建一個代理
? @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)建一個反序列化的代理
? 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;
? ? // 能夠觸發(fā)懶加載的方法名“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) { // 防止屬性的并發(fā)加載
? ? ? ? ? if (WRITE_REPLACE_METHOD.equals(methodName)) { // 被調用的是writeReplace方法
? ? ? ? ? ? // 創(chuàng)建一個原始對象
? ? ? ? ? ? Object original;
? ? ? ? ? ? if (constructorArgTypes.isEmpty()) {
? ? ? ? ? ? ? original = objectFactory.create(type);
? ? ? ? ? ? } else {
? ? ? ? ? ? ? original = objectFactory.create(type, constructorArgTypes, constructorArgs);
? ? ? ? ? ? }
? ? ? ? ? ? // 將被代理對象的屬性拷貝進入新創(chuàng)建的對象
? ? ? ? ? ? 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)) { // 設置了激進懶加載或者被調用的方法是能夠觸發(fā)全局懶加載的方法
? ? ? ? ? ? ? ? // 完成所有屬性的懶加載
? ? ? ? ? ? ? ? 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);
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? ? }
? ? ? ? }
? ? ? ? // 觸發(fā)被代理類的相應方法。能夠進行到這里的是除去writeReplace方法外的方法,例如讀寫方法、toString方法等
? ? ? ? return methodProxy.invokeSuper(enhanced, args);
? ? ? } catch (Throwable t) {
? ? ? ? throw ExceptionUtil.unwrapThrowable(t);
? ? ? }
? ? }
? }
? }代理類最核心的方法是intercept方法,當被代理對象的其他方法被調用時,intercept方法的處理方式是:
如果設置了激進懶加載或者被調用的是觸發(fā)全局加載的方法,則直接加載所有未加載的屬性。
如果被調用的是屬性寫方法,則將該方法從懶加載列表中刪除,因為此時數據庫中的數據已經不是最新的,沒有必要再去加載,然后進行屬性的寫入操作。
如果被調用的是讀方法,則該屬性尚未被懶加載的情況下,則加載該屬性,如果該屬性已經被懶加載過,則直接讀取該屬性。
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,經過一次序列化和反序列化后會變?yōu)閚ull
? ? 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 + "]");
? ? ? ? }
? ? ? ? // 創(chuàng)建結果對象的包裝
? ? ? ? this.metaResultObject = config.newMetaObject(userObject);
? ? ? ? // 創(chuàng)建結果加載器
? ? ? ? this.resultLoader = new ResultLoader(config, new ClosedExecutor(), ms, this.mappedParameter,
? ? ? ? ? ? ? ? metaResultObject.getSetterType(this.property), null, null);
? ? ? }
? ? ? // 只要經歷過持久化,則可能在別的線程中了。為這次惰性加載創(chuàng)建的新線程ResultLoader
? ? ? if (this.serializationCheck == null) {
? ? ? ? // 取出原來的ResultLoader中的必要信息,然后創(chuàng)建一個新的
? ? ? ? // 這是因為load函數可能在不同的時間多次執(zhí)行(第一次加載屬性A,又過了好久加載屬性B)。
? ? ? ? // 而該對象的各種屬性是跟隨對象的,加載屬性B時還保留著加載屬性A時的狀態(tài),即ResultLoader是加載屬性A時設置的
? ? ? ? // 則此時ResultLoader中的Executor在ResultLoader中被替換成了一個能運行的Executor,而不是ClosedExecutor
? ? ? ? // 能運行的Executor的狀態(tài)可能不是close,這將導致它被復用,從而引發(fā)多線程問題
? ? ? ? // 是不是被兩次執(zhí)行的一個關鍵點就是有沒有經過序列化,因為執(zhí)行完后會被序列化并持久化
? ? ? ? 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包懶加載功能 內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Dubbo在Spring和Spring Boot中的使用詳解
這篇文章主要介紹了Dubbo在Spring和Spring Boot中的使用詳解,需要的朋友可以參考下2017-10-10
SpringCloud @RefreshScope刷新機制深入探究
RefeshScope這個注解想必大家都用過,在微服務配置中心的場景下經常出現,他可以用來刷新Bean中的屬性配置,那大家對他的實現原理了解嗎?它為什么可以做到動態(tài)刷新呢2023-03-03
Intellij Idea 多模塊Maven工程中模塊之間無法相互引用問題
這篇文章主要介紹了Intellij Idea 多模塊Maven工程中模塊之間無法相互引用問題,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2021-01-01
SpringBoot如何使用RateLimiter通過AOP方式進行限流
這篇文章主要介紹了SpringBoot如何使用RateLimiter通過AOP方式進行限流,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-06-06

