zoukankan      html  css  js  c++  java
  • Mybatis框架解析之Builder解析

    首先我们从builder这个类入手,首先我们注意到BaseBuilder,其实它的本质上市一个抽象类,它从本质上抽象出了Builder的一切,我猜想这里一定使用了建造者模式,但是这个抽象类里面居然没有抽象方法!

    其中XXXValueOf方法,其实是把String字符串转换成了相对应的类型,如下代码。

      protected Boolean booleanValueOf(String value, Boolean defaultValue) {
        return value == null ? defaultValue : Boolean.valueOf(value);
      }
    
      protected Integer integerValueOf(String value, Integer defaultValue) {
        return value == null ? defaultValue : Integer.valueOf(value);
      }
    
      protected Set<String> stringSetValueOf(String value, String defaultValue) {
        value = (value == null ? defaultValue : value);
        return new HashSet<String>(Arrays.asList(value.split(",")));
      }
    View Code

    其中resoveXXXType的目的就是把string转换成相对应的类型。

      protected JdbcType resolveJdbcType(String alias) {
        if (alias == null) {
          return null;
        }
        try {
          return JdbcType.valueOf(alias);
        } catch (IllegalArgumentException e) {
          throw new BuilderException("Error resolving JdbcType. Cause: " + e, e);
        }
      }
    
      protected ResultSetType resolveResultSetType(String alias) {
        if (alias == null) {
          return null;
        }
        try {
          return ResultSetType.valueOf(alias);
        } catch (IllegalArgumentException e) {
          throw new BuilderException("Error resolving ResultSetType. Cause: " + e, e);
        }
      }
    
      protected ParameterMode resolveParameterMode(String alias) {
        if (alias == null) {
          return null;
        }
        try {
          return ParameterMode.valueOf(alias);
        } catch (IllegalArgumentException e) {
          throw new BuilderException("Error resolving ParameterMode. Cause: " + e, e);
        }
      }
    View Code

    下面的方法是通过字符串别名解析出相对应的类型,再从类型创建实例。

     protected Object createInstance(String alias) {
        Class<?> clazz = resolveClass(alias);
        if (clazz == null) {
          return null;
        }
        try {
          return resolveClass(alias).newInstance();
        } catch (Exception e) {
          throw new BuilderException("Error creating instance. Cause: " + e, e);
        }
      }
    
      protected Class<?> resolveClass(String alias) {
        if (alias == null) {
          return null;
        }
        try {
          return resolveAlias(alias);
        } catch (Exception e) {
          throw new BuilderException("Error resolving class. Cause: " + e, e);
        }
      }
    View Code

    注意下面的,是2个不同的重载类,是第一个调用第二个。首先得到相对应的TypeHanlder类型,如果该TypeHanlder在typeHanlderRegisty注册中心有留存,那么返回,否则从javatype里创建一个新的。

    protected TypeHandler<?> resolveTypeHandler(Class<?> javaType, String typeHandlerAlias) {
        if (typeHandlerAlias == null) {
          return null;
        }
        Class<?> type = resolveClass(typeHandlerAlias);
        if (type != null && !TypeHandler.class.isAssignableFrom(type)) {
          throw new BuilderException("Type " + type.getName() + " is not a valid TypeHandler because it does not implement TypeHandler interface");
        }
        @SuppressWarnings( "unchecked" ) // already verified it is a TypeHandler
        Class<? extends TypeHandler<?>> typeHandlerType = (Class<? extends TypeHandler<?>>) type;
        return resolveTypeHandler(javaType, typeHandlerType);
      }
    
      protected TypeHandler<?> resolveTypeHandler(Class<?> javaType, Class<? extends TypeHandler<?>> typeHandlerType) {
        if (typeHandlerType == null) {
          return null;
        }
        // javaType ignored for injected handlers see issue #746 for full detail
        TypeHandler<?> handler = typeHandlerRegistry.getMappingTypeHandler(typeHandlerType);
        if (handler == null) {
          // not in registry, create a new one
          handler = typeHandlerRegistry.getInstance(javaType, typeHandlerType);
        }
        return handler;
      }
    View Code

    其中MapperBuilderAssistant在此包下面,并且继承了BaseBuilder,下面对此类做一个解析,比如下面的就是解析命名空间的,就是包名。

      public String applyCurrentNamespace(String base, boolean isReference) {
        if (base == null) {
          return null;
        }
        if (isReference) {
          // is it qualified with any namespace yet?
          if (base.contains(".")) {
            return base;
          }
        } else {
          // is it qualified with this namespace yet?
          if (base.startsWith(currentNamespace + ".")) {
            return base;
          }
          if (base.contains(".")) {
            throw new BuilderException("Dots are not allowed in element names, please remove it from " + base);
          }
        }
        return currentNamespace + "." + base;
      }
    View Code

    下面的代码主要是用namespace得到cache的一个实例,就这么理解。

     public Cache useCacheRef(String namespace) {
        if (namespace == null) {
          throw new BuilderException("cache-ref element requires a namespace attribute.");
        }
        try {
          unresolvedCacheRef = true;
          Cache cache = configuration.getCache(namespace);
          if (cache == null) {
            throw new IncompleteElementException("No cache for namespace '" + namespace + "' could be found.");
          }
          currentCache = cache;
          unresolvedCacheRef = false;
          return cache;
        } catch (IllegalArgumentException e) {
          throw new IncompleteElementException("No cache for namespace '" + namespace + "' could be found.", e);
        }
      }
    View Code

    下面的方法是创建cache。

    public Cache useNewCache(Class<? extends Cache> typeClass,
          Class<? extends Cache> evictionClass,
          Long flushInterval,
          Integer size,
          boolean readWrite,
          boolean blocking,
          Properties props) {
        Cache cache = new CacheBuilder(currentNamespace)
            .implementation(valueOrDefault(typeClass, PerpetualCache.class))
            .addDecorator(valueOrDefault(evictionClass, LruCache.class))
            .clearInterval(flushInterval)
            .size(size)
            .readWrite(readWrite)
            .blocking(blocking)
            .properties(props)
            .build();
        configuration.addCache(cache);
        currentCache = cache;
        return cache;
      }
    View Code

    其中有一个地方要弄清楚,就是Class<? extends Cache> typeClass,    Class<? extends Cache> evictionClass的区别在哪里?在哪里呢?请看下图,一个是实现,一个是装饰者,你可以暂时理解为作用不同,就这么简单。

    下面是addParameterMap方法的一些介绍。

      public ParameterMap addParameterMap(String id, Class<?> parameterClass, List<ParameterMapping> parameterMappings) {
        
        //得到包名。
        id = applyCurrentNamespace(id, false);
        //工厂方法创建参数Map,并添加到configuration中去。
        ParameterMap parameterMap = new ParameterMap.Builder(configuration, id, parameterClass, parameterMappings).build();
        configuration.addParameterMap(parameterMap);
        return parameterMap;
      }

    下面是buildParameterMap的介绍,其实它也是利用了工厂方法骑构造。

      public ParameterMapping buildParameterMapping(
          Class<?> parameterType,
          String property,
          Class<?> javaType,
          JdbcType jdbcType,
          String resultMap,
          ParameterMode parameterMode,
          Class<? extends TypeHandler<?>> typeHandler,
          Integer numericScale) {
        resultMap = applyCurrentNamespace(resultMap, true);
    
        // Class parameterType = parameterMapBuilder.type();
        Class<?> javaTypeClass = resolveParameterJavaType(parameterType, property, javaType, jdbcType);
        TypeHandler<?> typeHandlerInstance = resolveTypeHandler(javaTypeClass, typeHandler);
    
        return new ParameterMapping.Builder(configuration, property, javaTypeClass)
            .jdbcType(jdbcType)
            .resultMapId(resultMap)
            .mode(parameterMode)
            .numericScale(numericScale)
            .typeHandler(typeHandlerInstance)
            .build();
      }
    View Code

    下面的是建立一个结果集,然后把结果集添加到configuration里面。

      public ResultMap addResultMap(
          String id,
          Class<?> type,
          String extend,
          Discriminator discriminator,
          List<ResultMapping> resultMappings,
          Boolean autoMapping) {
        id = applyCurrentNamespace(id, false);
        extend = applyCurrentNamespace(extend, true);
    
        if (extend != null) {
          if (!configuration.hasResultMap(extend)) {
            throw new IncompleteElementException("Could not find a parent resultmap with id '" + extend + "'");
          }
          ResultMap resultMap = configuration.getResultMap(extend);
          List<ResultMapping> extendedResultMappings = new ArrayList<ResultMapping>(resultMap.getResultMappings());
          extendedResultMappings.removeAll(resultMappings);
          // Remove parent constructor if this resultMap declares a constructor.
          boolean declaresConstructor = false;
          for (ResultMapping resultMapping : resultMappings) {
            if (resultMapping.getFlags().contains(ResultFlag.CONSTRUCTOR)) {
              declaresConstructor = true;
              break;
            }
          }
          if (declaresConstructor) {
            Iterator<ResultMapping> extendedResultMappingsIter = extendedResultMappings.iterator();
            while (extendedResultMappingsIter.hasNext()) {
              if (extendedResultMappingsIter.next().getFlags().contains(ResultFlag.CONSTRUCTOR)) {
                extendedResultMappingsIter.remove();
              }
            }
          }
          resultMappings.addAll(extendedResultMappings);
        }
        ResultMap resultMap = new ResultMap.Builder(configuration, id, type, resultMappings, autoMapping)
            .discriminator(discriminator)
            .build();
        configuration.addResultMap(resultMap);
        return resultMap;
      }
    View Code

    当然,下面的也太多了,就不一一介绍了,还有一些这些结果集的一些Getter方法;有兴趣的可以自己去看看,不过我们从这里得到了一个很重要的东西,那就是贯穿上下文的一个东西:Configuration!,这个东西可以说是无处不在,不管是在基类,还是在派生类中。

    我们还看到了一些工厂的Relover,那这些resover类其实也是调用了上面的一些public方法而已,没啥特别的,真的。

    下面我们再看看SqlSourceBuilder 这个类,这个先从string解析成map,然后再判断是否是sql类型,如果是,继续解析。

        private ParameterMapping buildParameterMapping(String content) {
          Map<String, String> propertiesMap = parseParameterMapping(content);
          String property = propertiesMap.get("property");
          Class<?> propertyType;
          if (metaParameters.hasGetter(property)) { // issue #448 get type from additional params
            propertyType = metaParameters.getGetterType(property);
          } else if (typeHandlerRegistry.hasTypeHandler(parameterType)) {
            propertyType = parameterType;
          } else if (JdbcType.CURSOR.name().equals(propertiesMap.get("jdbcType"))) {
            propertyType = java.sql.ResultSet.class;
          } else if (property == null || Map.class.isAssignableFrom(parameterType)) {
            propertyType = Object.class;
          } else {
            MetaClass metaClass = MetaClass.forClass(parameterType, configuration.getReflectorFactory());
            if (metaClass.hasGetter(property)) {
              propertyType = metaClass.getGetterType(property);
            } else {
              propertyType = Object.class;
            }
          }
          ParameterMapping.Builder builder = new ParameterMapping.Builder(configuration, property, propertyType);
          Class<?> javaType = propertyType;
          String typeHandlerAlias = null;
          for (Map.Entry<String, String> entry : propertiesMap.entrySet()) {
            String name = entry.getKey();
            String value = entry.getValue();
            if ("javaType".equals(name)) {
              javaType = resolveClass(value);
              builder.javaType(javaType);
            } else if ("jdbcType".equals(name)) {
              builder.jdbcType(resolveJdbcType(value));
            } else if ("mode".equals(name)) {
              builder.mode(resolveParameterMode(value));
            } else if ("numericScale".equals(name)) {
              builder.numericScale(Integer.valueOf(value));
            } else if ("resultMap".equals(name)) {
              builder.resultMapId(value);
            } else if ("typeHandler".equals(name)) {
              typeHandlerAlias = value;
            } else if ("jdbcTypeName".equals(name)) {
              builder.jdbcTypeName(value);
            } else if ("property".equals(name)) {
              // Do Nothing
            } else if ("expression".equals(name)) {
              throw new BuilderException("Expression based parameters are not supported yet");
            } else {
              throw new BuilderException("An invalid property '" + name + "' was found in mapping #{" + content + "}.  Valid properties are " + parameterProperties);
            }
          }
          if (typeHandlerAlias != null) {
            builder.typeHandler(resolveTypeHandler(javaType, typeHandlerAlias));
          }
          return builder.build();
        }
    View Code

    其中比较重要的就是下面的代码,下面做一个分析,首先会得到typeHanldler,然后再在buidler里对这个进行注册。

         if (typeHandlerAlias != null) {
            builder.typeHandler(resolveTypeHandler(javaType, typeHandlerAlias));
          }
          return builder.build();

    我们关键是看看builder.build方法,它是一个private的方法,它的作用就是get到我们开始设置的值,下面的validate方法也是做一些基础验证的,具体的可以略过,没啥价值。

        private void resolveTypeHandler() {
          if (parameterMapping.typeHandler == null && parameterMapping.javaType != null) {
            Configuration configuration = parameterMapping.configuration;
            TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
            parameterMapping.typeHandler = typeHandlerRegistry.getTypeHandler(parameterMapping.javaType, parameterMapping.jdbcType);
          }
        }

    下面我们再来看看这个类:StaticSqlSource 其实我觉得这个玩意没啥用!真的不是贬低写mybatis的人,真没看出有什么用,具体看下面的代码。

    public class StaticSqlSource implements SqlSource {
    
      private final String sql;
      private final List<ParameterMapping> parameterMappings;
      private final Configuration configuration;
    
      public StaticSqlSource(Configuration configuration, String sql) {
        this(configuration, sql, null);
      }
    
      public StaticSqlSource(Configuration configuration, String sql, List<ParameterMapping> parameterMappings) {
        this.sql = sql;
        this.parameterMappings = parameterMappings;
        this.configuration = configuration;
      }
    
      @Override
      public BoundSql getBoundSql(Object parameterObject) {
        return new BoundSql(configuration, sql, parameterMappings, parameterObject);
      }
    View Code

    这些类同级的包里面,还有一个XML的包,里面包含的DTD文件,以及一些工具类,大家理解这些东西,其实就是为了把烦人的XML转换成一个可用的configuration对象的的工具类就行了,真的没必要深究。

    关于builder的annotation 大家应该不陌生了吧?我介绍了这么多。构造函数说得很清楚了,其实把一些基本的注解加进了,CRUD而已。

      public MapperAnnotationBuilder(Configuration configuration, Class<?> type) {
        String resource = type.getName().replace('.', '/') + ".java (best guess)";
        this.assistant = new MapperBuilderAssistant(configuration, resource);
        this.configuration = configuration;
        this.type = type;
    
        sqlAnnotationTypes.add(Select.class);
        sqlAnnotationTypes.add(Insert.class);
        sqlAnnotationTypes.add(Update.class);
        sqlAnnotationTypes.add(Delete.class);
    
        sqlProviderAnnotationTypes.add(SelectProvider.class);
        sqlProviderAnnotationTypes.add(InsertProvider.class);
        sqlProviderAnnotationTypes.add(UpdateProvider.class);
        sqlProviderAnnotationTypes.add(DeleteProvider.class);
      }
    View Code

    有一个核心方法,比较重要:parse,作用很明显,就是转换呗,然后是从configuration拿玩意,然后转换成有用的东西。其实这个不就是我们写的mapper类的XML文件吗?!用过mybatis的人都知道的。注释写了一点,不过更深入了,我觉得没必要写了,靠大家自己去发掘。

      public void parse() {
        String resource = type.toString();
        if (!configuration.isResourceLoaded(resource)) {
          //载入XML资源文件。
          loadXmlResource();
          
          //把资源文件添加到configuation里面。
          configuration.addLoadedResource(resource);
          assistant.setCurrentNamespace(type.getName());
          //修改assistant变量
          parseCache();
          //修改assistant变量2
          parseCacheRef();
          Method[] methods = type.getMethods();
          for (Method method : methods) {
            try {
              // issue #237
              if (!method.isBridge()) {
                parseStatement(method);
              }
            } catch (IncompleteElementException e) {
              configuration.addIncompleteMethod(new MethodResolver(this, method));
            }
          }
        }
        parsePendingMethods();
      }
    View Code
  • 相关阅读:
    Google Map API使用详解(三)——Google Map基本常识(上)
    Google Map API使用详解(十)——使用JavaScript创建地图详解(上)
    sethc.exe
    taobao_java
    "void __cdecl operator delete[](void *)" (??_V@YAXPAX@Z) 已经在 LIBCMTD.lib(delete2.obj) 中定义 错误
    some Content
    变参 C++ 转帖
    阅读大型程序你得到什么
    一些模块
    a common jsp
  • 原文地址:https://www.cnblogs.com/kmsfan/p/8047878.html
Copyright © 2011-2022 走看看