mybatis初始化过程

mybatis初始化了什么

mybatis初始化工作,主要是初始化configuration配置信息,主要分为两部分

  • 从mybatis-config.xml中读取的配置
  • 从mapper配置文件或Mapper注解读取的配置

代码分析

mybatis初始化要经过简单的以下几步:

  1. 调用SqlSessionFactoryBuilder对象的build(inputStream)方法;

  2. SqlSessionFactoryBuilder会根据输入流inputStream等信息创建XMLConfigBuilder对象;

  3. XMLConfigBuilder对象初始化Configuration对象;

  4. XMLConfigBuilder调用parse方法,读取配置文件信息,更改Configuration对象属性信息,并返回Configuration对象

  5. SqlSessionFactoryBuilder根据Configuration对象创建一个DefaultSessionFactory对象;

  6. SqlSessionFactoryBuilder返回 DefaultSessionFactory对象给Client,供Client使用。

  7. 读取配置文件流程图


    configuration初始化流程.png

XMLConfigBuilder解析配置文件,并返回Configuration源码解析

 public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
    try {
      // xmlConfigBuilder读取配置文件信息,初始话Configuration信息
      XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
      //xmlConfigBuilder调用parse解析配置文件信息,返回Configuration
      return build(parser.parse());
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
      ErrorContext.instance().reset();
      try {
        reader.close();
      } catch (IOException e) {
        // Intentionally ignore. Prefer previous error.
      }
    }
  }
public XMLConfigBuilder(Reader reader, String environment, Properties props) {
//通过XPathParser解析xml    
this(new XPathParser(reader, true, props, new XMLMapperEntityResolver()), environment, props);
  }
  private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
    //new出一个Configuration对象,对象中有初始化的属性信息
    super(new Configuration());
    ErrorContext.instance().resource("SQL Mapper Configuration");
    this.configuration.setVariables(props);
    this.parsed = false;
    this.environment = environment;
    this.parser = parser;
  }
  public Configuration parse() {
    if (parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
    //解析configuration节点下的配置信息
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
  }

  private void parseConfiguration(XNode root) {
    try {
      //读取properties属性信息
      propertiesElement(root.evalNode("properties"));
      //读取别名信息
      typeAliasesElement(root.evalNode("typeAliases"));
      //读取插件信息
      pluginElement(root.evalNode("plugins"));
      objectFactoryElement(root.evalNode("objectFactory"));
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      reflectionFactoryElement(root.evalNode("reflectionFactory"));
     //读取全局配置属性信息
      settingsElement(root.evalNode("settings"));
      // 多环境读取配置
      environmentsElement(root.evalNode("environments"));
      //多数据源配置读取
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
        //读取类型转换器配置信息
      typeHandlerElement(root.evalNode("typeHandlers"));
       //读取mappers信息,包含resultMpper,sql语句信息
       mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }

propertiesElement源码解析

private void propertiesElement(XNode context) throws Exception {
    //对properties节点下配置信息进行解析
    if (context != null) {
      /*读取properties节点下所有的<property name="" value=""/>子节点
        *并以name为key,value为key的值注入到一个properties中,并返回
        */
      Properties defaults = context.getChildrenAsProperties();
      //获取properties节点下的节点resource配置信息路径
      String resource = context.getStringAttribute("resource");
      //获取properties节点下的节点url配置信息路径
      String url = context.getStringAttribute("url");
      //url和resource只能配置一个,否则抛出错误
      if (resource != null && url != null) {
        throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference.  Please specify one or the other.");
      }
      if (resource != null) {
        /*读取resource路径的配置信息,并返回,如果普通<property name=""           
        *value=""/> 有重复key配置,则以resource配置为准
      */
        defaults.putAll(Resources.getResourceAsProperties(resource));
      } else if (url != null) {
          /*读取url路径的配置信息,并返回,如果普通<property name=""           
        *value=""/> 有重复key配置,则以url 配置为准
      */
        defaults.putAll(Resources.getUrlAsProperties(url));
      }
      //此为初始化外部传入的properties配置信息
      Properties vars = configuration.getVariables();
      if (vars != null) {
         //配置文件配置信息 与 外部传入配置信息合并,如果有重复key配置,则以外部为准
        defaults.putAll(vars);
      }
      parser.setVariables(defaults);
      //更新configuration的属性Variables的值
      configuration.setVariables(defaults);
    }
  }

typeAliasesElement源码解析

  private void typeAliasesElement(XNode parent) {
    //解析节点typeAliases节点配置信息
    /*
   <typeAliases>
        <typeAlias type="com.study.entity.User" alias="User"/>
        <package name="com.study.entity"/>
    </typeAliases>
    */
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
        if ("package".equals(child.getName())) {
           //解析扫描包,进行别名配置
          String typeAliasPackage = child.getStringAttribute("name");
          // 扫描包下面的类,进行别名配置
          configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);
        } else {
          //获取别名
          String alias = child.getStringAttribute("alias");
          //获取别名 对应的类名称
          String type = child.getStringAttribute("type");
          try {
            Class<?> clazz = Resources.classForName(type);
            if (alias == null) {
            //注册别名   typeAliasRegistry对象和configuration.getTypeAliasRegistry()是同一个
              typeAliasRegistry.registerAlias(clazz);
            } else {
              //注册别名 typeAliasRegistry对象和configuration.getTypeAliasRegistry()是同一个
              typeAliasRegistry.registerAlias(alias, clazz);
            }
          } catch (ClassNotFoundException e) {
            throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);
          }
        }
      }
    }
  }
  public void registerAliases(String packageName){
    //根据包名,扫描包下面的类 ,进行注册别名配置
    registerAliases(packageName, Object.class);
  }

  public void registerAliases(String packageName, Class<?> superType){
    ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>();
    resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
     //获取包下面所有的类
    Set<Class<? extends Class<?>>> typeSet = resolverUtil.getClasses();
    for(Class<?> type : typeSet){
      //排除匿名类、接口类、成员类
      if (!type.isAnonymousClass() && !type.isInterface() && !type.isMemberClass()) {
        registerAlias(type);
      }
    }
  }
 public void registerAlias(Class<?> type) {
      //获取类的简单名称
    String alias = type.getSimpleName();
      //查看是否有别名注解
    Alias aliasAnnotation = type.getAnnotation(Alias.class);
    if (aliasAnnotation != null) {
      //有别名注解,则别名为注解的值
      alias = aliasAnnotation.value();
    } 
    registerAlias(alias, type);
  }
 public void registerAlias(String alias, Class<?> value) {
    //别名不可为空
    if (alias == null) {
      throw new TypeException("The parameter alias cannot be null");
    }
    //把别名小写
    String key = alias.toLowerCase(Locale.ENGLISH);
    //configuration中记录的别名如果存在当前别名,则抛出错误
    if (TYPE_ALIASES.containsKey(key) && TYPE_ALIASES.get(key) != null && !TYPE_ALIASES.get(key).equals(value)) {
      throw new TypeException("The alias '" + alias + "' is already mapped to the value '" + TYPE_ALIASES.get(key).getName() + "'.");
    }
    TYPE_ALIASES.put(key, value);
  }

pluginElement源码解析

 private void pluginElement(XNode parent) throws Exception {
    if (parent != null) {
      //获取节点plugins下面的配置信息
      for (XNode child : parent.getChildren()) {
        //获取插件类名称
        String interceptor = child.getStringAttribute("interceptor");
        //获取插件属性配置
        Properties properties = child.getChildrenAsProperties();
        //插件都是实现 org.apache.ibatis.plugin.Interceptor接口的类,把插件实例化
        Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
        //设置属性
        interceptorInstance.setProperties(properties);
        //在configuration插件属性中,增加插件
        configuration.addInterceptor(interceptorInstance);
      }
    }
  }

settingsElement源码解析

private void settingsElement(XNode context) throws Exception {
    if (context != null) {
      //获取所有setting配置参数信息<setting name="logImpl" value="LOG4J"/>
      Properties props = context.getChildrenAsProperties();
      // 确定所有的配置信息,是configuration可以识别的
      MetaClass metaConfig = MetaClass.forClass(Configuration.class, localReflectorFactory);
      for (Object key : props.keySet()) {
        if (!metaConfig.hasSetter(String.valueOf(key))) {
          throw new BuilderException("The setting " + key + " is not known.  Make sure you spelled it correctly (case sensitive).");
        }
      }
     /*指定 MyBatis 应如何自动映射列到字段或属性。
     NONE 表示取消自动映射;
     PARTIAL 只会自动映射没有定义嵌套结果集映射的结果集。 
      FULL 会自动映射任意复杂的结果集(无论是否嵌套)     
      */
 configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL")));
//全局地开启或关闭配置文件中的所有映射器已经配置的任何缓存。     configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true));
      //指定 Mybatis 创建具有延迟加载能力的对象所用到的代理工具。
      configuration.setProxyFactory((ProxyFactory) createInstance(props.getProperty("proxyFactory")));
/*延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。
 特定关联关系中可通过设置fetchType属性来覆盖该项的开关状态。
*/     
configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty("lazyLoadingEnabled"), false));
//当开启时,任何方法的调用都会加载该对象的所有属性。否则,每个属性会按需加载     configuration.setAggressiveLazyLoading(booleanValueOf(props.getProperty("aggressiveLazyLoading"), true));
//是否允许单一语句返回多结果集(需要兼容驱动)     configuration.setMultipleResultSetsEnabled(booleanValueOf(props.getProperty("multipleResultSetsEnabled"), true));
/*使用列标签代替列名。不同的驱动在这方面会有不同的表现, 
具体可参考相关驱动文档或通过测试这两种不同的模式来观察所用驱动的结果。
*/    
 configuration.setUseColumnLabel(booleanValueOf(props.getProperty("useColumnLabel"), true));
/*
允许 JDBC 支持自动生成主键,需要驱动兼容。 
如果设置为 true 则这个设置强制使用自动生成主键,尽管一些驱动不能兼容但仍可正常工作(比如 Derby)
*/     configuration.setUseGeneratedKeys(booleanValueOf(props.getProperty("useGeneratedKeys"), false));
/*
配置默认的执行器。
SIMPLE 就是普通的执行器;
REUSE 执行器会重用预处理语句(prepared statements);
BATCH 执行器将重用语句并执行批量更新
*/     configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE")));
  //设置超时时间,它决定驱动等待数据库响应的秒数。 configuration.setDefaultStatementTimeout(integerValueOf(props.getProperty("defaultStatementTimeout"), null));
//为驱动的结果集获取数量(fetchSize)设置一个提示值。此参数只可以在查询设置中被覆盖   
  configuration.setDefaultFetchSize(integerValueOf(props.getProperty("defaultFetchSize"), null));
 /*
是否开启自动驼峰命名规则(camel case)映射,即从经典数据库列名 A_COLUMN 到经典 Java 属性名 aColumn 的类似映射。
*/
 configuration.setMapUnderscoreToCamelCase(booleanValueOf(props.getProperty("mapUnderscoreToCamelCase"), false));
//允许在嵌套语句中使用分页(RowBounds)。如果允许使用则设置为false     configuration.setSafeRowBoundsEnabled(booleanValueOf(props.getProperty("safeRowBoundsEnabled"), false));
  /*
MyBatis 利用本地缓存机制(Local Cache)防止循环引用(circular references)和加速重复嵌套查询。 
默认值为 SESSION,这种情况下会缓存一个会话中执行的所有查询。 
若设置值为 STATEMENT,本地会话仅用在语句执行上,对相同 SqlSession 的不同调用将不会共享数据
*/  configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope", "SESSION")));
 /*
当没有为参数提供特定的 JDBC 类型时,为空值指定 JDBC 类型。
 某些驱动需要指定列的 JDBC 类型,多数情况直接用一般类型即可,
比如 NULL、VARCHAR 或 OTHER。
*/     configuration.setJdbcTypeForNull(JdbcType.valueOf(props.getProperty("jdbcTypeForNull", "OTHER")));
//  指定哪个对象的方法触发一次延迟加载     configuration.setLazyLoadTriggerMethods(stringSetValueOf(props.getProperty("lazyLoadTriggerMethods"), "equals,clone,hashCode,toString"));
  //允许在嵌套语句中使用分页(ResultHandler)。如果允许使用则设置为false    configuration.setSafeResultHandlerEnabled(booleanValueOf(props.getProperty("safeResultHandlerEnabled"), true));
 //指定动态 SQL 生成的默认语言。     configuration.setDefaultScriptingLanguage(resolveClass(props.getProperty("defaultScriptingLanguage")));
 /*
指定当结果集中值为 null 的时候是否调用映射对象的 setter(map 对象时为 put)方法,
这对于有 Map.keySet() 依赖或 null 值初始化的时候是有用的。
注意基本类型(int、boolean等)是不能设置成 null 的
*/     configuration.setCallSettersOnNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), false));
  //指定 MyBatis 增加到日志名称的前缀。
      configuration.setLogPrefix(props.getProperty("logPrefix"));
 // 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。
      configuration.setLogImpl(resolveClass(props.getProperty("logImpl")));
  /*
  指定一个提供Configuration实例的类。 
这个被返回的Configuration实例用来加载被反序列化对象的懒加载属性值。 
这个类必须包含一个签名方法static Configuration getConfiguration(). (从 3.2.3 版本开始)
*/  configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory")));
    }
  }

environmentsElement源码解析

 private void environmentsElement(XNode context) throws Exception {
    if (context != null) {
      if (environment == null) {
        //如果外部没有传入environment,则用default标签指定的environment 
        environment = context.getStringAttribute("default");
      }
      //获取所有的环境配置信息
      for (XNode child : context.getChildren()) {
        //后去当前节点,环境序号ID
        String id = child.getStringAttribute("id");
        //看当前节点id序号是否和上面environment一致,如果environment为空,则抛出错误
        if (isSpecifiedEnvironment(id)) {
          //获取事物管理配置信息 可以用别名,也可以指定类
          TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
        //获取连接池配置信息
          DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
          DataSource dataSource = dsFactory.getDataSource();
          //根据配置信息,new出环境environment的builder类,注入环境序号、事物管理、连接池信息
          Environment.Builder environmentBuilder = new Environment.Builder(id)
              .transactionFactory(txFactory)
              .dataSource(dataSource);
        //调用build()方法,返回Environment类,注入到configuration环境属性中
          configuration.setEnvironment(environmentBuilder.build());
        }
      }
    }
  }

databaseIdProviderElement源码解析

 private void databaseIdProviderElement(XNode context) throws Exception {
    DatabaseIdProvider databaseIdProvider = null;
/*配置案例
<databaseIdProvider type="DB_VENDOR">
        <property name="MySQL" value="mysql" />
        <property name="Oracle" value="oracle" />
    </databaseIdProvider>
*/
    if (context != null) {
      String type = context.getStringAttribute("type");
      // DB_VENDOR为别名,对应类
      //typeAliasRegistry.registerAlias("DB_VENDOR",VendorDatabaseIdProvider.class);
      if ("VENDOR".equals(type)) {
          type = "DB_VENDOR";
      }
      Properties properties = context.getChildrenAsProperties();
      databaseIdProvider = (DatabaseIdProvider) resolveClass(type).newInstance();
      databaseIdProvider.setProperties(properties);
    }
    Environment environment = configuration.getEnvironment();
    if (environment != null && databaseIdProvider != null) {
   /*获取databaseId,当databaseId不为空时,解析sql语句时,会过滤sql语句中databaseId不匹配的配置信息
    案例 此sql配置的databaseId是oracle,当前environment的数据库是mysql,则在读取配置信息时,会把这个sql配置信息过滤掉
 <insert id="insert" parameterType="com.study.entity.User" databaseId="oracle">
        insert into study_user (id,name,password,age,deleteFlag) values (#{id},#{name},#{password},#{age},#{deleteFlag})
    </insert>
    */
      String databaseId = databaseIdProvider.getDatabaseId(environment.getDataSource());
    //设置configuration的databaseId属性信息
      configuration.setDatabaseId(databaseId);
    }
  }

typeHandlerElement源码解析

 private void typeHandlerElement(XNode parent) throws Exception {
    //分析节点typeHandlers下的类型转换器配置信息
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
        if ("package".equals(child.getName())) {
          //通过扫描包,分析类型转换器配置信息
          String typeHandlerPackage = child.getStringAttribute("name");
          //类型转换器通过包名注册 类型转换器信息
          typeHandlerRegistry.register(typeHandlerPackage);
        } else {
           /*
            <typeHandlers>
               <typeHandler handler="" javaType="" jdbcType=""/>
               <package name=""/>
          </typeHandlers>
            */
          //获取节点typeHandler 的javaType
          String javaTypeName = child.getStringAttribute("javaType");
          //获取节点typeHandler 的jdbcType
          String jdbcTypeName = child.getStringAttribute("jdbcType");
          //获取节点typeHandler 的handler类
          String handlerTypeName = child.getStringAttribute("handler");
          //反射获取javaType的class
          Class<?> javaTypeClass = resolveClass(javaTypeName);
           //通过JdbcType.valueOf(alias),获取枚举JdbcType
          JdbcType jdbcType = resolveJdbcType(jdbcTypeName);
          //通过反射获取 类转换的Class  接口TypeHandler的实现实例
          Class<?> typeHandlerClass = resolveClass(handlerTypeName);
          if (javaTypeClass != null) {
            if (jdbcType == null) {
              //注册类转换信息
              typeHandlerRegistry.register(javaTypeClass, typeHandlerClass);
            } else {
               //注册类转换信息
              typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass);
            }
          } else {
             //注册类转换信息
            typeHandlerRegistry.register(typeHandlerClass);
          }
        }
      }
    }
  }
  • TypeHandlerRegistry的属性信息
  //jdbctype对应的typeHandler
  private final Map<JdbcType, TypeHandler<?>> JDBC_TYPE_HANDLER_MAP = new EnumMap<JdbcType, TypeHandler<?>>(JdbcType.class);
//Type是所有类的始祖,javaTYpe对应所有的jdbcType的typeHandler
  private final Map<Type, Map<JdbcType, TypeHandler<?>>> TYPE_HANDLER_MAP = new HashMap<Type, Map<JdbcType, TypeHandler<?>>>();
//未知的类转换器
  private final TypeHandler<Object> UNKNOWN_TYPE_HANDLER = new UnknownTypeHandler(this);
//class对应的类转换器
  private final Map<Class<?>, TypeHandler<?>> ALL_TYPE_HANDLERS_MAP = new HashMap<Class<?>, TypeHandler<?>>();
private <T> void register(Type javaType, TypeHandler<? extends T> typeHandler) {
//查看是否有   MappedJdbcTypes注解
 MappedJdbcTypes mappedJdbcTypes = typeHandler.getClass().getAnnotation(MappedJdbcTypes.class);
    if (mappedJdbcTypes != null) {
      for (JdbcType handledJdbcType : mappedJdbcTypes.value()) {
        //如果有注解则调用次方法,注册类转换器配置信息
        register(javaType, handledJdbcType, typeHandler);
      }
      if (mappedJdbcTypes.includeNullJdbcType()) {
        register(javaType, null, typeHandler);
      }
    } else {
      register(javaType, null, typeHandler);
    }
  }
private void register(Type javaType, JdbcType jdbcType, TypeHandler<?> handler) {
    if (javaType != null) {
  //查看是否已经有javaType的类转换配置信息
      Map<JdbcType, TypeHandler<?>> map = TYPE_HANDLER_MAP.get(javaType);
      if (map == null) {
      //没有则new出一个hashMap
        map = new HashMap<JdbcType, TypeHandler<?>>();
        TYPE_HANDLER_MAP.put(javaType, map);
      }
      map.put(jdbcType, handler);
    }
    //所有java类  类转换信息 加入配置信息
    ALL_TYPE_HANDLERS_MAP.put(handler.getClass(), handler);
  }

mapperElement源码解析

 private void mapperElement(XNode parent) throws Exception {
    if (parent != null) {
      //解析节点mappers 下的配置数据
      for (XNode child : parent.getChildren()) {
        //解析mppers下节点package,通过包扫描接口
        if ("package".equals(child.getName())) {
          //获取包名
          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) {
            ErrorContext.instance().resource(resource);
            //获取mapper.xml的inputStream流
            InputStream inputStream = Resources.getResourceAsStream(resource);
            //通过XMLMapperBuilder解析mapper.xml配置信息        SqlFragments为静态sql语句
            /*
        <sql id="studentProperties"><!--sql片段-->
        select 
            stud_id as studId
            , name, email
            , dob
            , phone
        from students
        </sql>
            可以通过<include refid="studentProperties"></include><!--复用-->c
            */      
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
            mapperParser.parse();
          } else if (resource == null && url != null && mapperClass == null) {
            ErrorContext.instance().resource(url);
            InputStream inputStream = Resources.getUrlAsStream(url);
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
            mapperParser.parse();
          } else if (resource == null && url == null && mapperClass != null) {
            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.");
          }
        }
      }
    }
  }
  • 扫描包mapper解析源码分析
 public void addMappers(String packageName) {
    mapperRegistry.addMappers(packageName);
  }
  • mapperRegistry类属性
//全局configuration
private final Configuration config;
//接口  对应的代理类工厂   和spring集成需要用到这个  
 private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<Class<?>, MapperProxyFactory<?>>();
  public <T> void addMapper(Class<T> type) {
    //解析接口类
    if (type.isInterface()) {
      //接口类如果已经解析过,则抛出错误
      if (hasMapper(type)) {
        throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
      }
      boolean loadCompleted = false;
      try {
      //在knownMappers中放入接口类,接口类代理工厂的信息
        knownMappers.put(type, new MapperProxyFactory<T>(type));
         //new 出MapperAnnotationBuilder注解解析器
        MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
        parser.parse();
        loadCompleted = true;
      } finally {
        if (!loadCompleted) {
          knownMappers.remove(type);
        }
      }
    }
  }
  • 通过 MapperAnnotationBuilder进行解析
 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注解
    sqlAnnotationTypes.add(Select.class);
    sqlAnnotationTypes.add(Insert.class);
    sqlAnnotationTypes.add(Update.class);
    sqlAnnotationTypes.add(Delete.class);
     //sqlProviderAnnotationTypes注解  两种注解不可以同时存在
    sqlProviderAnnotationTypes.add(SelectProvider.class);
    sqlProviderAnnotationTypes.add(InsertProvider.class);
    sqlProviderAnnotationTypes.add(UpdateProvider.class);
    sqlProviderAnnotationTypes.add(DeleteProvider.class);
  }

  public void parse() {
    String resource = type.toString();
    //查看是否已经加载过
    if (!configuration.isResourceLoaded(resource)) {
      //查找下面的xml文件,接口同名以'.xml'结尾
      loadXmlResource();
      configuration.addLoadedResource(resource);
      assistant.setCurrentNamespace(type.getName());
      parseCache();
      parseCacheRef();
      //根据方法注解,来解析mapper接口
      Method[] methods = type.getMethods();
      for (Method method : methods) {
        try {
          //过滤掉桥接方法
          if (!method.isBridge()) {
            //解析方法,如果当前命名空间+method名称已经解析过,则抛出错误
            /*Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection");
              重写了put方法,会判断是否key已经存在,存在会抛出错误
            */
            parseStatement(method);
          }
        } catch (IncompleteElementException e) {
          configuration.addIncompleteMethod(new MethodResolver(this, method));
        }
      }
    }
    parsePendingMethods();
  }
  • XMLMapperBuilder解析xml过程解析
public void parse() {
    if (!configuration.isResourceLoaded(resource)) {
      //解析xml文件下mapper节点下的配置信息
      configurationElement(parser.evalNode("/mapper"));
      //记录该资源已经解析过
      configuration.addLoadedResource(resource);
      bindMapperForNamespace();
    }

    parsePendingResultMaps();
    parsePendingChacheRefs();
    parsePendingStatements();
  }
 private void configurationElement(XNode context) {
    try {
       //获取命名空间名称
      String namespace = context.getStringAttribute("namespace");
      if (namespace == null || namespace.equals("")) {
        throw new BuilderException("Mapper's namespace cannot be empty");
      }
      //解析助理 设置当前命名空间名称
      builderAssistant.setCurrentNamespace(namespace);
       //缓存配置解析
      cacheRefElement(context.evalNode("cache-ref"));
       //缓存配置解析
      cacheElement(context.evalNode("cache"));
      //不建议使用
      parameterMapElement(context.evalNodes("/mapper/parameterMap"));
      //resultMap解析
      resultMapElements(context.evalNodes("/mapper/resultMap"));
      //sql解析
      sqlElement(context.evalNodes("/mapper/sql"));
      //增删改查sql语句解析
      buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);
    }
  }

   Mybatis初始化过程中,解析parameterMap、resultMap、"select|insert|update|delete"元素,无疑是重头戏
  元素parameterMap将会解析为ParameterMap对象,该对象包含一个List<ParameterMapping>集合,是one-to-many关系。
   元素resultMap将会解析为ResultMap对象,该对象包含一个List<ResultMapping>集合,是one-to-many关系。
  元素"select|insert|update|delete"将会被解析为MappedStatement对象,该对象包含了ParameterMap、ResultMap等对象

  • resultMap解析
    • ResultMap 类属性
  //resultMap的id值
  private String id;
  //对应的java类
  private Class<?> type;
   //所有的resultMapping对象,包括constructor/idArg,constructor/arg,result,association,collection,
  //但不包括association和collection里的子节点
  private List<ResultMapping> resultMappings;
  //包括constructor/idArg,id
  private List<ResultMapping> idResultMappings;
  //constructor里的子节点
  private List<ResultMapping> constructorResultMappings;
  //除constructor里的子节点,其他都是,result,association,collection,id 
  private List<ResultMapping> propertyResultMappings;
  //所有被映射的列 
  private Set<String> mappedColumns;
  //通过case来判断映射类,比较少用 
  private Discriminator discriminator;
   //是否有内映射,association, collection都为内映射,
  //内查询不算(就是的reulst节点中配置select属性的情况)
  private boolean hasNestedResultMaps;
  //是否有查询,  
  private boolean hasNestedQueries;
  //是否要求自动映射  
  private Boolean autoMapping;
    • ResultMap.Builder.build()方法
 public ResultMap build() {
      //id不可为空
      if (resultMap.id == null) {
        throw new IllegalArgumentException("ResultMaps must have an id");
      }
      //映射的列
      resultMap.mappedColumns = new HashSet<String>();
      //包括constructor/idArg,id
      resultMap.idResultMappings = new ArrayList<ResultMapping>();
      //constructor里的子节点
      resultMap.constructorResultMappings = new ArrayList<ResultMapping>();
      //除constructor里的子节点,其他都是
      resultMap.propertyResultMappings = new ArrayList<ResultMapping>();
      for (ResultMapping resultMapping : resultMap.resultMappings) {
        //如果其中一个resultMapping有内查询,则这个resultMap也就是有内查询
        resultMap.hasNestedQueries = resultMap.hasNestedQueries || resultMapping.getNestedQueryId() != null;
        //如果其中一个resultMapping有内映射,则这个resultMap也就是有内映射 
        resultMap.hasNestedResultMaps = resultMap.hasNestedResultMaps || (resultMapping.getNestedResultMapId() != null && resultMapping.getResultSet() == null);
        //获取字段名称
        final String column = resultMapping.getColumn();
        if (column != null) {
          resultMap.mappedColumns.add(column.toUpperCase(Locale.ENGLISH));
        } else if (resultMapping.isCompositeResult()) {
        //组合配置分析
          for (ResultMapping compositeResultMapping : resultMapping.getComposites()) {
            final String compositeColumn = compositeResultMapping.getColumn();
            if (compositeColumn != null) {
              resultMap.mappedColumns.add(compositeColumn.toUpperCase(Locale.ENGLISH));
            }
          }
        }
        //如果是构造器resultMapping,则放入constructorResultMappings
        if (resultMapping.getFlags().contains(ResultFlag.CONSTRUCTOR)) {
          resultMap.constructorResultMappings.add(resultMapping);
        } else {
      //其余的放入propertyResultMappings
          resultMap.propertyResultMappings.add(resultMapping);
        }
         //ID的类型resultMapping放入idResultMappings
        if (resultMapping.getFlags().contains(ResultFlag.ID)) {
          resultMap.idResultMappings.add(resultMapping);
        }
      }
      //如果没有指定ID,idResultMappings包含所有的resultMapping
      if (resultMap.idResultMappings.isEmpty()) {
        resultMap.idResultMappings.addAll(resultMap.resultMappings);
      }
      //集合为只读的
      resultMap.resultMappings = Collections.unmodifiableList(resultMap.resultMappings);
      resultMap.idResultMappings = Collections.unmodifiableList(resultMap.idResultMappings);
      resultMap.constructorResultMappings = Collections.unmodifiableList(resultMap.constructorResultMappings);
      resultMap.propertyResultMappings = Collections.unmodifiableList(resultMap.propertyResultMappings);
      resultMap.mappedColumns = Collections.unmodifiableSet(resultMap.mappedColumns);
      return resultMap;
    }
  • 增删改查sql语句解析
    • 一个MappedStatement对象对应Mapper配置文件中的一个select/update/insert/delete节点,主要描述的是一条SQL语句
/*
 * MappedStatement所有的属性方法
*/

  private String resource;
  private Configuration configuration;
  //节点中的id属性加要命名空间 
  private String id;
  private Integer fetchSize;
  //超时时间
  private Integer timeout;
  //statement类型 STATEMENT, PREPARED, CALLABLE
  private StatementType statementType;
  //结果集类型 
  private ResultSetType resultSetType;
  //对应一条SQL语句 动态sql语句封装在这里
  private SqlSource sqlSource;
  private Cache cache;
  private ParameterMap parameterMap;
  private List<ResultMap> resultMaps;
  private boolean flushCacheRequired;
  //是否启用缓存
  private boolean useCache;
  private boolean resultOrdered;
  //SQL的类型,select/update/insert/detete  
  private SqlCommandType sqlCommandType;
  private KeyGenerator keyGenerator;
  private String[] keyProperties;
  private String[] keyColumns;
  //是否有内映射  
  private boolean hasNestedResultMaps;
  //数据库ID
  private String databaseId;
  private Log statementLog;
  private LanguageDriver lang;
  private String[] resultSets;
 public void parseStatementNode() {
      //获取id属性
    String id = context.getStringAttribute("id");
    String databaseId = context.getStringAttribute("databaseId");

    if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
      return;
    }

    Integer fetchSize = context.getIntAttribute("fetchSize");
    Integer timeout = context.getIntAttribute("timeout");
    String parameterMap = context.getStringAttribute("parameterMap");
    String parameterType = context.getStringAttribute("parameterType");
    Class<?> parameterTypeClass = resolveClass(parameterType);
    String resultMap = context.getStringAttribute("resultMap");
    String resultType = context.getStringAttribute("resultType");
    String lang = context.getStringAttribute("lang");
    LanguageDriver langDriver = getLanguageDriver(lang);

    Class<?> resultTypeClass = resolveClass(resultType);
    String resultSetType = context.getStringAttribute("resultSetType");
    StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
    ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);

    String nodeName = context.getNode().getNodeName();
    SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
    boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
    boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
    boolean useCache = context.getBooleanAttribute("useCache", isSelect);
    boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);

    // Include Fragments before parsing
    XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
    includeParser.applyIncludes(context.getNode());

    // 解析selectKey
    processSelectKeyNodes(id, parameterTypeClass, langDriver);
    
    // 解析sql语句
    SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
    String resultSets = context.getStringAttribute("resultSets");
    String keyProperty = context.getStringAttribute("keyProperty");
    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();
    }
    • SqlSource 解析
      • SqlSource接口
public interface SqlSource {

  BoundSql getBoundSql(Object parameterObject);

}

public class BoundSql {
  //解析后的最终sql语句,可能带有问号 "?"
  private String sql;
  //参数
  private List<ParameterMapping> 
  parameterMappings;
  private Object parameterObject;
  private Map<String, Object> additionalParameters;
  private MetaObject metaParameters;
}
public SqlSource parseScriptNode() {
    //解析sql语句,把节点下的sql语句信息,封装到SqlNode中
    List<SqlNode> contents = parseDynamicTags(context);
    MixedSqlNode rootSqlNode = new MixedSqlNode(contents);
    SqlSource sqlSource = null;
    //是否是动态
    if (isDynamic) {
      sqlSource = new DynamicSqlSource(configuration, rootSqlNode);
    } else {
      sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);
    }
    return sqlSource;
  }
    //递归调用此方法
  List<SqlNode> parseDynamicTags(XNode node) {
    List<SqlNode> contents = new ArrayList<SqlNode>();
    NodeList children = node.getNode().getChildNodes();
    for (int i = 0; i < children.getLength(); i++) {
      XNode child = node.newXNode(children.item(i));
      if (child.getNode().getNodeType() == Node.CDATA_SECTION_NODE || child.getNode().getNodeType() == Node.TEXT_NODE) {
        String data = child.getStringBody("");
        TextSqlNode textSqlNode = new TextSqlNode(data);
        //是否是动态 ${}为动态
        if (textSqlNode.isDynamic()) {
          contents.add(textSqlNode);
          isDynamic = true;
        } else {
          contents.add(new StaticTextSqlNode(data));
        }
      } 
//含有set if where trim等节点 
else if (child.getNode().getNodeType() == Node.ELEMENT_NODE) { 
        String nodeName = child.getNode().getNodeName();
   //获取对应的nodeHandler,所有类型的nodeHandler看下面的代码      
  NodeHandler handler = nodeHandlers(nodeName);
        if (handler == null) {
          throw new BuilderException("Unknown element <" + nodeName + "> in SQL statement.");
        }
        //解析此节点,会递归调用当前方法
        handler.handleNode(child, contents);
        isDynamic = true;
      }
    }
    return contents;
  }
  //所有nodeHandler类型
  NodeHandler nodeHandlers(String nodeName) {
    Map<String, NodeHandler> map = new HashMap<String, NodeHandler>();
    map.put("trim", new TrimHandler());
    map.put("where", new WhereHandler());
    map.put("set", new SetHandler());
    map.put("foreach", new ForEachHandler());
    map.put("if", new IfHandler());
    map.put("choose", new ChooseHandler());
    map.put("when", new IfHandler());
    map.put("otherwise", new OtherwiseHandler());
    map.put("bind", new BindHandler());
    return map.get(nodeName);
  }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,302评论 5 470
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,232评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,337评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,977评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,920评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,194评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,638评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,319评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,455评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,379评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,426评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,106评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,696评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,786评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,996评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,467评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,043评论 2 341

推荐阅读更多精彩内容