广告
返回顶部
首页 > 资讯 > 精选 >mybatis中如何实现executor包懒加载功能 
  • 929
分享到

mybatis中如何实现executor包懒加载功能 

2023-06-29 04:06:59 929人浏览 独家记忆
摘要

这篇文章主要为大家展示了“mybatis中如何实现executor包懒加载功能 ”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“mybatis中如何实现executor包懒加载功能&nb

这篇文章主要为大家展示了“mybatis中如何实现executor包懒加载功能 ”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“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个实现类,CglibProxyFactoryJavassistProxyFactor类。这两个实现类整体结构高度一致,内部类、方法设置都一样,只是实现原理不同。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;    }        @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);    }        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包懒加载功能 ”这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注编程网精选频道!

--结束END--

本文标题: mybatis中如何实现executor包懒加载功能 

本文链接: https://www.lsjlt.com/news/322680.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • mybatis中如何实现executor包懒加载功能 
    这篇文章主要为大家展示了“mybatis中如何实现executor包懒加载功能 ”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“mybatis中如何实现executor包懒加载功能&nb...
    99+
    2023-06-29
  • mybatis源码解读之executor包懒加载功能
    ProxyFactory是创建代理类的工厂接口,其中的setProperties方法用来对工厂进行属性设置,但是mybatis内置的两个实现类都没有实现该接口,所以不支持属性设置。c...
    99+
    2022-11-13
  • MyBatis懒加载如何实现
    这篇文章主要介绍“MyBatis懒加载如何实现”,在日常操作中,相信很多人在MyBatis懒加载如何实现问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”MyBatis懒加载如何实现”的疑惑有所帮助!接下来,请跟...
    99+
    2023-06-30
  • javascript如何实现图片预加载和懒加载功能
    这篇文章主要介绍“javascript如何实现图片预加载和懒加载功能”,在日常操作中,相信很多人在javascript如何实现图片预加载和懒加载功能问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”javascr...
    99+
    2023-06-14
  • Mybatis如何实现关联属性懒加载
    Mybatis 关联属性懒加载 延迟加载配置 mybatis默认没有开启延迟加载,需要在config.xml中setting配置。 lazyLoadingEnabled:true使用...
    99+
    2022-11-12
  • webpack如何实现懒加载和预加载
    小编给大家分享一下webpack如何实现懒加载和预加载,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!正常加载为了看的方便,index.js中的代码非常简单console.log('index.js执行了')...
    99+
    2023-06-22
  • android fragment懒加载如何实现
    Android Fragment的懒加载可以通过以下步骤实现:1. 在Fragment类中添加一个boolean类型的变量isLoa...
    99+
    2023-08-26
    android fragment
  • 使用objc runtime实现iOS闭环的懒加载功能
    使用objc runtime实现懒加载 地址:AutoPropertyCocoa 懒加载形式如下 - (id)lazyloadProperty{ if(_lazyloadP...
    99+
    2022-06-05
    objc runtime ios 懒加载
  • 如何通过IntersectionObserver实现懒加载
    通常懒加载等都会通过监听scroll事件用getBoundingClientRect()来判断元素位置来决定是否可以开始加载。性能开销是比较大的,为了节省性能又需要各种操作去弥补,例...
    99+
    2023-05-16
    IntersectionObserver懒加载
  • vue2组件如何实现懒加载
    这篇文章给大家分享的是有关vue2组件如何实现懒加载的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。一、 什么是懒加载懒加载也叫延迟加载,即在需要的时候进行加载,随用随载。二、为什...
    99+
    2022-10-19
  • vue如何实现路由懒加载
    在vue中实现路由懒加载的方法有:1.使用import方法实现;2.使用require.ensure()方法实现;3.使用vue异步组件实现;具体方法如下:使用import方法实现路由懒加载const Home = () => import...
    99+
    2022-10-20
  • JavaScript如何实现图片懒加载
    这篇文章主要介绍了JavaScript如何实现图片懒加载,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。图片懒加载imagegetBoundClientRect 的实现方式,监...
    99+
    2023-06-27
  • 详解如何在Java中实现懒加载
    目录Java 中如何实现懒加载使用 Supplier 接口实现懒加载使用双重检查锁定模式实现懒加载Java 中如何实现懒加载 懒加载是一种常见的优化技术,它可以延迟对象的创建或初始化...
    99+
    2023-03-19
    Java实现懒加载 Java懒加载
  • React+TS+IntersectionObserver实现视频懒加载和自动播放功能
    目录IntersectionObserver视频播放控制组件结语为了提高用户体验,网站通常会使用视频来呈现内容。然而,在网站中使用自动播放的视频通常会导致页面加载时间变长,因为用户需...
    99+
    2023-05-18
    React懒加载 React视频自动播放
  • vue-router如何实现路由懒加载
    这篇文章主要为大家展示了“vue-router如何实现路由懒加载”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“vue-router如何实现路由懒加载”这篇文章吧...
    99+
    2022-10-19
  • 如何实现前端图片懒加载
    这篇文章主要介绍了如何实现前端图片懒加载,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。定义图片懒加载又称图片延时加载、惰性加载,即在用户需要...
    99+
    2022-10-19
  • js前端如何实现图片懒加载
    这篇文章主要介绍js前端如何实现图片懒加载,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!思路:将页面里所有img属性src属性用data-xx代替,当页面滚动直至此图片出现在可视区域...
    99+
    2022-10-19
  • 小程序列表懒加载如何实现
    本文小编为大家详细介绍“小程序列表懒加载如何实现”,内容详细,步骤清晰,细节处理妥当,希望这篇“小程序列表懒加载如何实现”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。小程序上的列表懒加载长列表我们经常接触到,长列...
    99+
    2023-06-29
  • Bootstrap中Table如何实现加载按钮功能
    小编给大家分享一下Bootstrap中Table如何实现加载按钮功能,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!1 ...
    99+
    2022-10-19
  • mui如何实现上拉加载功能
    这篇文章给大家分享的是有关mui如何实现上拉加载功能的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。最近在做移动端的项目,用到了mui的上拉加载,整理如下:1、需要引入的css、j...
    99+
    2022-10-19
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作