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;
    }

    /**
     * 代理类的拦截方法
     * @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包懒加载功能 内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Mybatis原始执行方式Executor代码实例解析

    一.通过SqlSessionFactory创建sqlsession,再由Sqlsession获取session对象,然后通过session中的执行器Executor,去执行MapperStatement封装的sql语句 @Test public void findAll() throws IOException { //1.读取配置文件 InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml"); //2.创建

  • mybatis源码解读之executor包语句处理功能

    1.mybatis对多语句类型的支持 在mybatis映射文件中传参数,主要用到#{} 或者 ${}. #{}:表示使用这种符号的变量会以预编译的形式赋值到sql片段中. ${}:表示使用这种符号的变量会以字符串的形式直接插到sql片段中. mybatis中支持三种语句类型,不同语句类型支持的变量符号不同.mybatis的三种类型如下: STATEMENT:这种语句类型中,只会对sql片段进行简单的字符串拼接.只支持使用${}. PREPARED:这种语句中会先对sql片段进行字符串拼接,然后再

  • MyBatis 源码分析 之SqlSession接口和Executor类

    mybatis框架在操作数据的时候,离不开SqlSession接口实例类的作用.可以说SqlSession接口实例是开发过程中打交道最多的一个类.即是DefaultSqlSession类.如果笔者记得没有错的话,早期是没有什么getMapper方法的.增删改查各志有对应的方法进行操作.虽然现在改进了很多,但是也保留了很多.我们依旧可以看到类似于selectList这样子的方法.源码的例子里面就可以找到.如下 SqlSession session = sqlMapper.openSession(T

  • Mybatis中SqlSession下的四大对象之执行器(executor)

    首先我先解释一下标题 四大对象是指:executor, statementHandler,parameterHandler,resultHandler对象.(为了方便下面的文章说道四大对象就专指它们) 它们都是sqlSession的底层类实现,也是插件能够拦截的四大对象.所以这里已经触及了MyBATIS的底层,动态代理,反射随时可以看到,如果没有第一篇作为基础,你将十分难以理解它.了解他们的协作,是插件编写的基础之一,所以这是十分的重要. Executor在sqlSession中的应用 上篇我们

  • mybatis源码解读之executor包懒加载功能 

    ProxyFactory是创建代理类的工厂接口,其中的setProperties方法用来对工厂进行属性设置,但是mybatis内置的两个实现类都没有实现该接口,所以不支持属性设置.createProxy方法用来创建一个代理对象 public interface ProxyFactory {   // 设置工厂属性   default void setProperties(Properties properties) {   }   // 创建代理对象   Object createProxy(O

  • mybatis源码解读-Java中executor包的语句处理功能

    目录 1.mybatis对多语句类型的支持 2.mybatis的语句处理功能 1.mybatis对多语句类型的支持 在mybatis映射文件中传参数,主要用到#{} 或者 ${}. #{}:表示使用这种符号的变量会以预编译的形式赋值到sql片段中. ${}:表示使用这种符号的变量会以字符串的形式直接插到sql片段中. mybatis中支持三种语句类型,不同语句类型支持的变量符号不同.mybatis的三种类型如下: STATEMENT:这种语句类型中,只会对sql片段进行简单的字符串拼接.只支持使

  • CI框架源码解读之利用Hook.php文件完成功能扩展的方法

    本文实例讲述了CI框架源码解读之利用Hook.php文件完成功能扩展的方法.分享给大家供大家参考,具体如下: 看了hook.php的源码,就知道CI使用hook来进行扩展的原理了. hook的基本知识 http://codeigniter.org.cn/user_guide/general/hooks.html CI中hook的使用经历了一个:开启hook,定义hook,调用hook,执行hook的过程. 手册中已经告知了开启.定义.调用的方法.那么hook的实现原理是啥呢. <?php if

  • SpringBoot源码分析之bootstrap.properties文件加载的原理

    目录 1.bootstrap的使用 2.bootstrap加载原理分析 2.1 BootstrapApplicationListener 2.2 启动流程梳理 2.3 bootstrap.properties的加载原理   对于SpringBoot中的属性文件相信大家在工作中用的是比较多的,对于application.properties和application.yml文件应该非常熟悉,但是对于bootstrap.properties文件和bootstrap.yml这个两个文件用的估计就比较少了

  • 使用objc runtime实现iOS闭环的懒加载功能

    使用objc runtime实现懒加载 地址:AutoPropertyCocoa 懒加载形式如下 - (id)lazyloadProperty{ if(_lazyloadProperty == nil){ _lazyloadProperty = [XClass ...]; } return _lazyloadProperty; } 一般使用宏定义可以轻松完成.但是没有一致性,移植差. 利用objc runtime的动态性实现懒加载可以实现即可增加又可删除功能,也可以避免污染类型.该三方弥补了目前

  • vue 实现图片懒加载功能

    一个页面图片比较多的时候,需要对界面的图片进行懒加载处理,今天遇到了,做个懒加载的笔记. 1,需要安装vue的懒加载插件. npm install vue-lazyload --save-dev 2,需要在main.js里面进行引用. import VueLazyload from "vue-lazyload"; Vue.use(VueLazyload); 或者自定义 Vue.use(VueLazyload, { preLoad: 1.3, error: 'dist/error.png

  • 基于angular6.0实现的一个组件懒加载功能示例

    我们常常会遇到这样一个问题,当我们使用一个第三方控件库的时候,我们只用到了其中 1 个或某几个组件,会连带一大堆无用的东西,造成体积臃肿不堪.又或者首页用到的组件较多,首页加载速度缓慢,这个时候,我们或许需要加载用户可视范围内用到的组件,随着用户的浏览下拉,我们再去加载这些组件,渐进式加载,渐进式体验,这个时候你或许就用到了本工具所实现的功能.或者一个页面的某些不重要区域,比如第三方广告又或者不重要的元素,可以采用懒加载懒渲染,降低用户首屏等待时间.一切都在用户不知不觉中进行.大大增加用户体验,

  • vue2+elementUI的el-tree的懒加载功能

    lazy 属性为true 时生效lazy ----> 是否懒加载子节点,需与 load 方法结合使用 isLeaf可以提前告知 Tree 某个节点是否为叶子节点,从而避免在叶子节点前渲染下拉按钮. HTML部分 <el-tree class="filter-tree" ref="tree" accordion :data="leftData" @node-click="handleNodeClick" node-k

  • seajs1.3.0源码解析之module依赖有序加载

    这里是seajs loader的核心部分,有些IE兼容的部分还不是很明白,主要是理解各个模块如何依赖有序加载,以及CMD规范. 代码有点长,需要耐心看: 复制代码 代码如下: /** * The core of loader */ ;(function(seajs, util, config) { // 模块缓存 var cachedModules = {} // 接口修改缓存 var cachedModifiers = {} // 编译队列 var compileStack = [] // 模

随机推荐