zoukankan      html  css  js  c++  java
  • MyBatis源码解读(二)

    MyBatis缓存

    如果一级、二级缓存同时开启的话。会先去查询二级缓存再去查询一级缓存。

    二级缓存的开启配置我们就不多做介绍,我们主要进行二级缓存开启后的执行流程。

    1、开启二级缓存

    以下代码就是我们解析配置文件的时候进行解析的代码。会解析到我们开启二级缓存的配置。

    1、
    public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
        try {
            //委托XMLConfigBuilder来解析xml文件,并构建
          XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
          return build(parser.parse());
        } catch (Exception e) {
            //这里是捕获异常,包装成自己的异常并抛出的idiom?,最后还要reset ErrorContext
          throw ExceptionFactory.wrapException("Error building SqlSession.", e);
        } finally {
          ErrorContext.instance().reset();
          try {
            reader.close();
          } catch (IOException e) {
            // Intentionally ignore. Prefer previous error.
          }
        }
      }
    2、//追parser.parse()中的mapperElement(root.evalNode("mappers"));
      //因为这个方法解析的是我们的Mapper配置文件。
        private void mapperElement(XNode parent) throws Exception {
        if (parent != null) {
          for (XNode child : parent.getChildren()) {
            if ("package".equals(child.getName())) {
              //10.4自动扫描包下所有映射器
              String mapperPackage = child.getStringAttribute("name");
              configuration.addMappers(mapperPackage);
            } else {
              String resource = child.getStringAttribute("resource");
              String url = child.getStringAttribute("url");
              String mapperClass = child.getStringAttribute("class");
              if (resource != null && url == null && mapperClass == null) {
                //10.1使用类路径
                ErrorContext.instance().resource(resource);
                InputStream inputStream = Resources.getResourceAsStream(resource);
                //映射器比较复杂,调用XMLMapperBuilder
                //注意在for循环里每个mapper都重新new一个XMLMapperBuilder,来解析
                XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
                //这个方法就开始解析映射配置文件的。
                mapperParser.parse();
              } else if (resource == null && url != null && mapperClass == null) {
                //10.2使用绝对url路径
                ErrorContext.instance().resource(url);
                InputStream inputStream = Resources.getUrlAsStream(url);
                //映射器比较复杂,调用XMLMapperBuilder
                XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
                mapperParser.parse();
              } else if (resource == null && url == null && mapperClass != null) {
                //10.3使用java类名
                Class<?> mapperInterface = Resources.classForName(mapperClass);
                //直接把这个映射加入配置
                configuration.addMapper(mapperInterface);
              } else {
                throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
              }
            }
          }
        }
      }
    
    3、//mapperParser.parse();
        //解析
      public void parse() {
        //如果没有加载过再加载,防止重复加载
        if (!configuration.isResourceLoaded(resource)) {
          //开始解析mapper根节点
          configurationElement(parser.evalNode("/mapper"));
          //标记一下,已经加载过了
          configuration.addLoadedResource(resource);
          //绑定映射器到namespace
          bindMapperForNamespace();
        }
    
        //还有没解析完的东东这里接着解析?  
        parsePendingResultMaps();
        parsePendingChacheRefs();
        parsePendingStatements();
      }
    4、//configurationElement(parser.evalNode("/mapper"));
         private void configurationElement(XNode context) {
        try {
          //1.配置namespace
          String namespace = context.getStringAttribute("namespace");
          if (namespace.equals("")) {
            throw new BuilderException("Mapper's namespace cannot be empty");
          }
          builderAssistant.setCurrentNamespace(namespace);
          //2.配置cache-ref
          cacheRefElement(context.evalNode("cache-ref"));
          //3.配置cache 这就是我们要找的Cache。
          cacheElement(context.evalNode("cache"));
          //4.配置parameterMap(已经废弃,老式风格的参数映射)
          parameterMapElement(context.evalNodes("/mapper/parameterMap"));
          //5.配置resultMap(高级功能)
          resultMapElements(context.evalNodes("/mapper/resultMap"));
          //6.配置sql(定义可重用的 SQL 代码段)
          sqlElement(context.evalNodes("/mapper/sql"));
          //7.配置select|insert|update|delete TODO
          buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
        } catch (Exception e) {
          throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);
        }
      }
    5、//终于找到了我们想要的Cache节点解析方法。
        private void cacheElement(XNode context) throws Exception {
        if (context != null) {
          String type = context.getStringAttribute("type", "PERPETUAL");
          Class<? extends Cache> typeClass = typeAliasRegistry.resolveAlias(type);
          String eviction = context.getStringAttribute("eviction", "LRU");
          Class<? extends Cache> evictionClass = typeAliasRegistry.resolveAlias(eviction);
          Long flushInterval = context.getLongAttribute("flushInterval");
          Integer size = context.getIntAttribute("size");
          boolean readWrite = !context.getBooleanAttribute("readOnly", false);
          boolean blocking = context.getBooleanAttribute("blocking", false);
          //读入额外的配置信息,易于第三方的缓存扩展,例:
    //    <cache type="com.domain.something.MyCustomCache">
    //      <property name="cacheFile" value="/tmp/my-custom-cache.tmp"/>
    //    </cache>
          Properties props = context.getChildrenAsProperties();
          //调用builderAssistant.useNewCache
          builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWrite, blocking, props);
        }
      }
    6、//解析完我们配置的参数便调用了下面:builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWrite, blocking, props);
     public Cache useNewCache(Class<? extends Cache> typeClass,
          Class<? extends Cache> evictionClass,
          Long flushInterval,
          Integer size,
          boolean readWrite,
          boolean blocking,
          Properties props) {
          //这里面又判断了一下是否为null就用默认值,有点和XMLMapperBuilder.cacheElement逻辑重复了
        typeClass = valueOrDefault(typeClass, PerpetualCache.class);
        evictionClass = valueOrDefault(evictionClass, LruCache.class);
        //调用CacheBuilder构建cache,id=currentNamespace
        Cache cache = new CacheBuilder(currentNamespace)
            .implementation(typeClass)
            .addDecorator(evictionClass)
            .clearInterval(flushInterval)
            .size(size)
            .readWrite(readWrite)
            .blocking(blocking)
            .properties(props)
            .build();
        //将我们的缓存配置加入到了核心配置类当中
        configuration.addCache(cache);
        //当前的缓存
        currentCache = cache;
        return cache;
      }
    
    

    经过上面源码流程,我们得知的缓存对象的创建。但是还没完,还有一部分select、update、delete标签中的属性要解析:我们要将创建的缓存对象放入到Statement中去。

    1、
    private void configurationElement(XNode context) {
        try {
          //1.配置namespace
          String namespace = context.getStringAttribute("namespace");
          if (namespace.equals("")) {
            throw new BuilderException("Mapper's namespace cannot be empty");
          }
          builderAssistant.setCurrentNamespace(namespace);
          //2.配置cache-ref
          cacheRefElement(context.evalNode("cache-ref"));
          //3.配置cache
          cacheElement(context.evalNode("cache"));
          //4.配置parameterMap(已经废弃,老式风格的参数映射)
          parameterMapElement(context.evalNodes("/mapper/parameterMap"));
          //5.配置resultMap(高级功能)
          resultMapElements(context.evalNodes("/mapper/resultMap"));
          //6.配置sql(定义可重用的 SQL 代码段)
          sqlElement(context.evalNodes("/mapper/sql"));
          //7.配置select|insert|update|delete TODO
          buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
        } catch (Exception e) {
          throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);
        }
      }
    
    2、// buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
        
         private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
        for (XNode context : list) {
          //构建所有语句,一个mapper下可以有很多select
          //语句比较复杂,核心都在这里面,所以调用XMLStatementBuilder
          final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
          try {
              //核心XMLStatementBuilder.parseStatementNode
            statementParser.parseStatementNode();
          } catch (IncompleteElementException e) {
              //如果出现SQL语句不完整,把它记下来,塞到configuration去
            configuration.addIncompleteStatement(statementParser);
          }
        }
      }
    3、//statementParser.parseStatementNode();主要是去解析select|insert|update|delete这些标签,这里面也涉及到一些缓存配置。
        public void parseStatementNode() {
        String id = context.getStringAttribute("id");
        String databaseId = context.getStringAttribute("databaseId");
    
        //如果databaseId不匹配,退出
        if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
          return;
        }
    
        //暗示驱动程序每次批量返回的结果行数
        Integer fetchSize = context.getIntAttribute("fetchSize");
        //超时时间
        Integer timeout = context.getIntAttribute("timeout");
        //引用外部 parameterMap,已废弃
        String parameterMap = context.getStringAttribute("parameterMap");
        //参数类型
        String parameterType = context.getStringAttribute("parameterType");
        Class<?> parameterTypeClass = resolveClass(parameterType);
        //引用外部的 resultMap(高级功能)
        String resultMap = context.getStringAttribute("resultMap");
        //结果类型
        String resultType = context.getStringAttribute("resultType");
        //脚本语言,mybatis3.2的新功能
        String lang = context.getStringAttribute("lang");
        //得到语言驱动
        LanguageDriver langDriver = getLanguageDriver(lang);
    
        Class<?> resultTypeClass = resolveClass(resultType);
        //结果集类型,FORWARD_ONLY|SCROLL_SENSITIVE|SCROLL_INSENSITIVE 中的一种
        String resultSetType = context.getStringAttribute("resultSetType");
        //语句类型, STATEMENT|PREPARED|CALLABLE 的一种
        StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
        ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
    
        //获取命令类型(select|insert|update|delete)
        String nodeName = context.getNode().getNodeName();
        SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
        boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
        boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
        //是否要缓存select结果
        boolean useCache = context.getBooleanAttribute("useCache", isSelect);
        //仅针对嵌套结果 select 语句适用:如果为 true,就是假设包含了嵌套结果集或是分组了,这样的话当返回一个主结果行的时候,就不会发生有对前面结果集的引用的情况。
        //这就使得在获取嵌套的结果集的时候不至于导致内存不够用。默认值:false。 
        boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);
    
        // Include Fragments before parsing
        //解析之前先解析<include>SQL片段
        XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
        includeParser.applyIncludes(context.getNode());
    
        // Parse selectKey after includes and remove them.
        //解析之前先解析<selectKey>
        processSelectKeyNodes(id, parameterTypeClass, langDriver);
        
        // Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
        //解析成SqlSource,一般是DynamicSqlSource
        SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
        String resultSets = context.getStringAttribute("resultSets");
        //(仅对 insert 有用) 标记一个属性, MyBatis 会通过 getGeneratedKeys 或者通过 insert 语句的 selectKey 子元素设置它的值
        String keyProperty = context.getStringAttribute("keyProperty");
        //(仅对 insert 有用) 标记一个属性, MyBatis 会通过 getGeneratedKeys 或者通过 insert 语句的 selectKey 子元素设置它的值
        String keyColumn = context.getStringAttribute("keyColumn");
        KeyGenerator keyGenerator;
        String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
        keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
        if (configuration.hasKeyGenerator(keyStatementId)) {
          keyGenerator = configuration.getKeyGenerator(keyStatementId);
        } else {
          keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
              configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
              ? new Jdbc3KeyGenerator() : new NoKeyGenerator();
        }
    
    	//又去调助手类,通过该方法创建MappedStatement
        builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
            fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
            resultSetTypeEnum, flushCache, useCache, resultOrdered, 
            keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
      }  
    
    4、/**builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
            fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
            resultSetTypeEnum, flushCache, useCache, resultOrdered, 
            keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
      } **/ 
     //增加映射语句
      public MappedStatement addMappedStatement(
          String id,
          SqlSource sqlSource,
          StatementType statementType,
          SqlCommandType sqlCommandType,
          Integer fetchSize,
          Integer timeout,
          String parameterMap,
          Class<?> parameterType,
          String resultMap,
          Class<?> resultType,
          ResultSetType resultSetType,
          boolean flushCache,
          boolean useCache,
          boolean resultOrdered,
          KeyGenerator keyGenerator,
          String keyProperty,
          String keyColumn,
          String databaseId,
          LanguageDriver lang,
          String resultSets) {
        
        if (unresolvedCacheRef) {
          throw new IncompleteElementException("Cache-ref not yet resolved");
        }
        
        //为id加上namespace前缀
        id = applyCurrentNamespace(id, false);
        //是否是select语句
        boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
    
        //又是建造者模式
        MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType);
        statementBuilder.resource(resource);
        statementBuilder.fetchSize(fetchSize);
        statementBuilder.statementType(statementType);
        statementBuilder.keyGenerator(keyGenerator);
        statementBuilder.keyProperty(keyProperty);
        statementBuilder.keyColumn(keyColumn);
        statementBuilder.databaseId(databaseId);
        statementBuilder.lang(lang);
        statementBuilder.resultOrdered(resultOrdered);
        statementBuilder.resulSets(resultSets);
        setStatementTimeout(timeout, statementBuilder);
    
        //1.参数映射
        setStatementParameterMap(parameterMap, parameterType, statementBuilder);
        //2.结果映射
        setStatementResultMap(resultMap, resultType, resultSetType, statementBuilder);
        //将之前封装好的缓存对象currentCache设置进去。
        setStatementCache(isSelect, flushCache, useCache, currentCache, statementBuilder);
    
        MappedStatement statement = statementBuilder.build();
        //建造好调用configuration.addMappedStatement
        configuration.addMappedStatement(statement);
        return statement;
      }
    
        
    

    2、二级缓存执行流程

    2.1

    既然二级缓存都已经配置好了,那我们看看二级缓存是怎样使用的呐。

    我们直接在selectList入手:

    //核心selectList
      @Override
      public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
        try {
          //根据statement id找到对应的MappedStatement
          MappedStatement ms = configuration.getMappedStatement(statement);
          //转而用执行器来查询结果,注意这里传入的ResultHandler是null,注意这个执行器,如果开启了缓存,他就是CachingExecutor
          return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
        } catch (Exception e) {
          throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
        } finally {
          ErrorContext.instance().reset();
        }
      }
    
    

    我们继续追一下query方法:

    @Override
      public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
          throws SQLException {
        //得到缓存对象,我们以前准备好存进去的。  
        Cache cache = ms.getCache();
        //默认情况下是没有开启缓存的(二级缓存).要开启二级缓存,你需要在你的 SQL 映射文件中添加一行: <cache/>
        //简单的说,就是先查CacheKey,查不到再委托给实际的执行器去查
        if (cache != null) {
          flushCacheIfRequired(ms);
          if (ms.isUseCache() && resultHandler == null) {
            ensureNoOutParams(ms, parameterObject, boundSql);
            @SuppressWarnings("unchecked")
            //从二级缓存中获取对应结果。
            List<E> list = (List<E>) tcm.getObject(cache, key);
            if (list == null) {
               //这个方法先去执行一级缓存,delegate是simpleExecutor执行器。 
              list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
              tcm.putObject(cache, key, list); // issue #578 and #116
            }
            return list;
          }
        }
        return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
      }
    

    2.2

    既然已经知道了执行流程,接下来看下二级缓存具体的存取操作:

     //得到某个TransactionalCache的值
      public Object getObject(Cache cache, CacheKey key) {
        return getTransactionalCache(cache).getObject(key);
      }
    
    //getTransactionalCache(cache)
      //管理了许多TransactionalCache
    private Map<Cache, TransactionalCache> transactionalCaches = new HashMap<Cache, TransactionalCache>();
    
    private TransactionalCache getTransactionalCache(Cache cache) {
        TransactionalCache txCache = transactionalCaches.get(cache);
        if (txCache == null) {
          txCache = new TransactionalCache(cache);
          transactionalCaches.put(cache, txCache);
        }
        return txCache;
      }
      
    //getObject(key) 通过上面得到的TransactionalCache对象通过key去获取缓存。
     @Override
      public Object getObject(Object key) {
        Object object = delegate.getObject(key);
        if (object == null) {
          entriesMissedInCache.add(key);
        }
        if (clearOnCommit) {
          return null;
        } else {
          return object;
        }
      }
    
    //Cache是一个接口,不同的缓存类型对象要去实现它。
    

    2.3

    不过还没完,以目前所说的并不能解决事物问题,我们去看看事物问题是如何解决的?

      //缓存对象,里面存着缓存。
      private Cache delegate;
      //commit时要不要清缓存
      private boolean clearOnCommit;
      //事物被提交前,所有从数据库中查询的结果缓存在这里面
      private Map<Object, Object> entriesToAddOnCommit;
      //事物被提交前,当缓存未命中是,CacheKey被存在这个集合中
      private Set<Object> entriesMissedInCache;
    
    //put
     @Override
      public void putObject(Object key, Object object) {
        //添加时不直接放入缓存对象。
        entriesToAddOnCommit.put(key, object);
      }
    //commit
     public void commit() {
        //判断commit的时候判断要不要删除以前的缓存
        if (clearOnCommit) {
          delegate.clear();
        }
        flushPendingEntries();
        reset();
      }
    1、
    private void flushPendingEntries() {
        for (Map.Entry<Object, Object> entry : entriesToAddOnCommit.entrySet()) {
          //开始存放到真正的缓存当中,所以说不commit根本查不到
          delegate.putObject(entry.getKey(), entry.getValue());
        }
        for (Object entry : entriesMissedInCache) {
          if (!entriesToAddOnCommit.containsKey(entry)) {
            //没命中且不在entriesToAddOnCommit,就是两个缓存都没有,直接赋值为空
            delegate.putObject(entry, null);
          }
        }
      }
    
      private void unlockMissedEntries() {
        for (Object entry : entriesMissedInCache) {
          delegate.putObject(entry, null);
        }
      }
    }
    2、  private void reset() {
        clearOnCommit = false;
        entriesToAddOnCommit.clear();
        entriesMissedInCache.clear();
      }
    
    
    

    MyBatis延迟加载

    延迟加载就是当数据使用的时候再加载进来。例子就是用户关联订单,我们在查询时,首先查询出用户数据,当用到订单数据时再去加载。

    同样,延迟加载配置开启就不做过多介绍,我们来探讨它的部分源码。

    延迟加载的原理就是实现目标对象的代理对象。

    UserDao userDao = sqlSession.getMapper(UserDao.class);

    上图返回的userDao对象为代理对象。我们调用它关联的数据对象的get方法时就会进入拦截器,判断是否需要延迟加载,所以就进入了准备好的SQL将关联对象查出来。

    1、代理对象的创建

    下面展示为结果对象创建代理对象的代码。

    private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {
        final List<Class<?>> constructorArgTypes = new ArrayList<Class<?>>();
        final List<Object> constructorArgs = new ArrayList<Object>();
        //创建映射后的结果对象
        final Object resultObject = createResultObject(rsw, resultMap, constructorArgTypes, constructorArgs, columnPrefix);
        if (resultObject != null && !typeHandlerRegistry.hasTypeHandler(resultMap.getType())) {
          //如果有内嵌查询,并且开启了延迟加载,则创建结果对象的的代理对象
          //得到我们再配置文件ResultMapping中配置的属性
          //每一个ResultMapping对应xml文件中的一个配置。
          final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
          for (ResultMapping propertyMapping : propertyMappings) {
            //判断ResultMapping里面子标签有没有配置延迟加载
            if (propertyMapping.getNestedQueryId() != null && propertyMapping.isLazy()) {
              //TODO 使用代理(cglib/javaassist)去创建队里对象。
              return configuration.getProxyFactory().createProxy(resultObject, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
            }
          }
        }
        return resultObject;
      }
    //默认使用Javassist
    //protected ProxyFactory proxyFactory = new JavassistProxyFactory();
    

    既然找到的代理工厂我们便去看看代理工厂创建代理对象的代码。

    public static Object createProxy(Object target, ResultLoaderMap lazyLoader, Configuration configuration, ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
          final Class<?> type = target.getClass();
        //代理类执行时就会去执行它的invoke
          EnhancedResultObjectProxyImpl callback = new EnhancedResultObjectProxyImpl(type, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
        //生成代理方法
          Object enhanced = crateProxy(type, callback, constructorArgTypes, constructorArgs);
          PropertyCopier.copyBeanProperties(type, target, enhanced);
          return enhanced;
        }
    

    2、代理对象的执行

    代理对象已经创建完成,我们就可以去看看代理对象的执行过程了。

    @Override
        public Object invoke(Object enhanced, Method method, Method methodProxy, Object[] args) throws Throwable {
          final String methodName = method.getName();
          try {
            synchronized (lazyLoader) {
              if (WRITE_REPLACE_METHOD.equals(methodName)) {
                Object original = null;
                if (constructorArgTypes.isEmpty()) {
                  original = objectFactory.create(type);
                } else {
                  original = objectFactory.create(type, constructorArgTypes, constructorArgs);
                }
                PropertyCopier.copyBeanProperties(type, enhanced, original);
                if (lazyLoader.size() > 0) {
                  return new JavassistSerialStateHolder(original, lazyLoader.getProperties(), objectFactory, constructorArgTypes, constructorArgs);
                } else {
                  return original;
                }
              } else {
                if (lazyLoader.size() > 0 && !FINALIZE_METHOD.equals(methodName)) {
                  //加载所有延迟加载的属性。
                  if (aggressive || lazyLoadTriggerMethods.contains(methodName)) {
                    lazyLoader.loadAll();
                    //判断必须以get|set|is开头。
                  } else if (PropertyNamer.isProperty(methodName)) {
                    //根据方法名替换为属性。
                    final String property = PropertyNamer.methodToProperty(methodName);
                    //判断这个属性是否是延迟加载的属性。
                    if (lazyLoader.hasLoader(property)) {
                      //根据延迟属性进行加载
                      lazyLoader.load(property);
                    }
                  }
                }
              }
            }
                  //执行原方法
            return methodProxy.invoke(enhanced, args);
          } catch (Throwable t) {
            throw ExceptionUtil.unwrapThrowable(t);
          }
        }
    
  • 相关阅读:
    HDU 1075 What Are You Talking About(字典树)
    HDU 1075 What Are You Talking About (stl之map映射)
    HDU 1247 Hat’s Words(字典树活用)
    字典树HihoCoder
    HDU 1277全文检索(字典树)
    HDU 3294 Girls' research(manachar模板题)
    HDU 3294 Girls' research(manachar模板题)
    HDU 4763 Theme Section(KMP灵活应用)
    Ordering Tasks UVA
    Abbott's Revenge UVA
  • 原文地址:https://www.cnblogs.com/kenai/p/14918499.html
Copyright © 2011-2022 走看看