Mybatis 之 mybatis-config.xml 子节点的详解篇

双篇同线进行 mybatis-config.xml 文件的解析。本篇的目的就是深入函数解析。XNode 是 xml 的标签解析得到的存储属性、子节点的信息类。

1. properties

  • propertiesElement(root.evalNode(“properties”))

1.1 context.getChildrenAsProperties()

/* 循环遍历子节点,获取 name,value 值,生成键值对并返回 */
public Properties getChildrenAsProperties() {
  Properties properties = new Properties();
  for (XNode child : getChildren()) {
    String name = child.getStringAttribute("name");
    String value = child.getStringAttribute("value");
    if (name != null && value != null) {
      properties.setProperty(name, value);
    }
  }
  return properties;
}

1.2 context.getStringAttribute(“XXX”)

/* name: 属性名称,def: 指定属性默认值,未指定则未 null */
public String getStringAttribute(String name) {
  return getStringAttribute(name, null);
}
public String getStringAttribute(String name, String def) {
  String value = attributes.getProperty(name);
  if (value == null) {
    return def;
  } else {
    return value;
  }
}

1.3 Resources.getResourceAsProperties(resource)

  • 获取 resource 指定的资源文件,然后通过 Properties 读取
  • Resources 类的函数
/* 使用了1.8的特性之 try-with-resources */
public static Properties getResourceAsProperties(String resource) throws IOException {
  Properties props = new Properties();
  try (InputStream in = getResourceAsStream(resource)) {
    props.load(in);
  }
  return props;
}
  • getResourceAsStream():底层使用 ClassLoader 加载资源
/* 重载函数作用,指定没有传入的参数为 null */
public static InputStream getResourceAsStream(String resource) throws IOException {
  return getResourceAsStream(null, resource);
}
/* 通过 class加载器 与 resource 获取输入流 */
public static InputStream getResourceAsStream(ClassLoader loader, String resource) throws IOException {
  InputStream in = classLoaderWrapper.getResourceAsStream(resource, loader);
  if (in == null) {
    throw new IOException("Could not find resource " + resource);
  }
  return in;
}
  • ClassLoaderWrapper 类的函数
public InputStream getResourceAsStream(String resource, ClassLoader classLoader) {
  return getResourceAsStream(resource, getClassLoaders(classLoader));
}
/* 真正获取资源的方法 */
/*
* 1. 循环类加载器数组classLoader
* 2. 判断类加载器cl是否为null,若不为null则通过resource路径加载资源
* 3. 由于某些类加载器可能需要路径以"/"开头,若returnValue 为null则添加"/"并再次尝试
* 4. 若 returnValue 不为null则将其返回,否则继续循环,直接结束还未找到则返回 null
*/
InputStream getResourceAsStream(String resource, ClassLoader[] classLoader) {
  for (ClassLoader cl : classLoader) {
    if (null != cl) {
      InputStream returnValue = cl.getResourceAsStream(resource);
      if (null == returnValue) {
        returnValue = cl.getResourceAsStream("/" + resource);
      }
      if (null != returnValue) {
        return returnValue;
      }
    }
  }
  return null;
}
  • getClassLoaders(classLoader):获取类加载器的数组
/*
* classLoader:传入的类加载器
* defaultClassLoader:默认类加载器
* Thread.currentThread().getContextClassLoader(): 当前线程的 context 类加载器
* getClass().getClassLoader():当前类的类加载器
* systemClassLoader:系统类加载器
*/
ClassLoader[] getClassLoaders(ClassLoader classLoader) {
  return new ClassLoader[]{
      classLoader,
      defaultClassLoader,
      Thread.currentThread().getContextClassLoader(),
      getClass().getClassLoader(),
      systemClassLoader};
}

1.4 getUrlAsStream()

  • 获取连接通道,从这也可看出 url 是获取网络资源 or 本地系统的文件
public static InputStream getUrlAsStream(String urlString) throws IOException {
  URLurl = new URL(urlString);
  URLConnection conn = url.openConnection();
  return conn.getInputStream();
}

2. settings

2.1 MetaClass.forClass()

  • 首先来看 MetaClass 类
/* reflectorFactory: 反射工厂, reflector: 用于存储类信息 */
public class MetaClass {
  private final ReflectorFactory reflectorFactory;
  private final Reflector reflector;
}
  • 先细看 ReflectorFactory 接口及其默认实现类
/* 接口,定义了三个抽象方法 */
public interface ReflectorFactory {
  boolean isClassCacheEnabled();
  void setClassCacheEnabled(boolean classCacheEnabled);
  Reflector findForClass(Class<?> type);
}
/* ReflectorFactory 的默认实现类,后面需要用到 */
public class DefaultReflectorFactory implements ReflectorFactory {
  private boolean classCacheEnabled = true;
  private final ConcurrentMap<Class<?>, Reflector> reflectorMap = new ConcurrentHashMap<>();

  public DefaultReflectorFactory() {
  }
  @Override
  public boolean isClassCacheEnabled() {
    return classCacheEnabled;
  }
  @Override
  public void setClassCacheEnabled(boolean classCacheEnabled) {
    this.classCacheEnabled = classCacheEnabled;
  }
  /* 若使用缓存(默认开启),则从reflectorMap获取
   * computeIfAbsent():存在则获取,不存在则 new 一个放进去
   * jdk1.8新增,Reflector::new <==> new Reflector(),但这里有参数,实际上调用的是 new Reflector(type)
   * */
  @Override
  public Reflector findForClass(Class<?> type) {
    if (classCacheEnabled) {
      return reflectorMap.computeIfAbsent(type, Reflector::new);
    } else {
      return new Reflector(type);
    }
  }
}
  • 再看 Reflector 类
public class Reflector {
  /* class 类型 */
  private final Class<?> type;
  /* 可读属性数组 */
  private final String[] readablePropertyNames;
  /* 可写属性数组 */
  private final String[] writablePropertyNames;
  /* set方法集合 */
  private final Map<String, Invoker> setMethods = new HashMap<>();
  /* get方法集合 */
  private final Map<String, Invoker> getMethods = new HashMap<>();
  /* setter方法中参数值类型的Map,key为属性名称,value为对应的参数类型 */
  private final Map<String, Class<?>> setTypes = new HashMap<>();
  /* getter方法中返回值类型的Map,key为属性名称,value为对应的返回值类型 */
  private final Map<String, Class<?>> getTypes = new HashMap<>();
  /* 默认构造器 */
  private Constructor<?> defaultConstructor;
  /* 所有属性名称的集合,key为属性名称大写,value为对应的属性名称 */
  private Map<String, String> caseInsensitivePropertyMap = new HashMap<>();
}  
  • 了解完基础类信息,来看 forClass() 方法就简单许多
/* 传入 type 类型与 反射工厂,返回一个 MetaClass 的实例 */
public static MetaClass forClass(Class<?> type, ReflectorFactory reflectorFactory) {
  return new MetaClass(type, reflectorFactory);
}
/* MetaClass的构造方法,findForClass() 回看reflectorFactory的实现类,一般为DefaultReflectorFactory */
private MetaClass(Class<?> type, ReflectorFactory reflectorFactory) {
  this.reflectorFactory = reflectorFactory;
  this.reflector = reflectorFactory.findForClass(type);
}

2.2 metaConfig.hasSetter()

  • 是否有属性对应的 setter 方法
public boolean hasSetter(String name) {
  PropertyTokenizer prop = new PropertyTokenizer(name);
  if (prop.hasNext()) {
    if (reflector.hasSetter(prop.getName())) {
      MetaClass metaProp = metaClassForProperty(prop.getName());
      return metaProp.hasSetter(prop.getChildren());
    } else {
      return false;
    }
  } else {
    return reflector.hasSetter(prop.getName());
  }
}
  • 来看 PropertyTokenizer 类(只看属性猜类的作用)
/* 实现 Iterator,即为一个迭代器
 * name: 属性名称
 * indexedName:当前遍历属性名称
 * index: 当前下标?(还没用到,等回头确认)
 * children: 孩子节点名称?(还没用到,等回头确认)
 *  */
public class PropertyTokenizer implements Iterator<PropertyTokenizer> {
  private String name;
  private final String indexedName;
  private String index;
  private final String children;
}
  • reflector.hasSetter(prop.getName()):属性名称是否有对应的 setter 方法
/* setMethods: 就是一个 HashMap */
public boolean hasSetter(String propertyName) {
  return setMethods.keySet().contains(propertyName);
}

2.3 Resources.classForName()

  • 通过类名加载类
public static Class<?> classForName(String className) throws ClassNotFoundException {
  return classLoaderWrapper.classForName(className);
}
  • ClassLoaderWrapper 类
public Class<?> classForName(String name) throws ClassNotFoundException {
  return classForName(name, getClassLoaders(null));
}
/* 底层使用 ClassLoader 类加载器,通过 类名 加载类信息,找不到返回 null */
Class<?> classForName(String name, ClassLoader[] classLoader) throws ClassNotFoundException {
  for (ClassLoader cl : classLoader) {
    if (null != cl) {
      try {
        Class<?> c = Class.forName(name, true, cl);
        if (null != c) {
          return c;
        }
      } catch (ClassNotFoundException e) {
        // we'll ignore this until all classloaders fail to locate the class
      }
    }
  }
  throw new ClassNotFoundException("Cannot find class: " + name);
}

2.4 resolveClass(String alias)

protected <T> Class<? extends T> resolveClass(String alias) {
  if(alias == null) {
    return null;
  }
  try {
    return resolveAlias(alias);
  } catch (Exception e) {
    throw new BuilderException("Error resolving class. Cause: " + e, e);
  }
}

protected <T> Class<? extends T> resolveAlias(String alias) {
  return typeAliasRegistry.resolveAlias(alias);
}
  • TypeAliasRegistry 类
/* typeAliases: 就是一个 HashMap */
@SuppressWarnings("unchecked")
public <T> Class<T> resolveAlias(String string) {
  try {
    if (string == null) {
      return null;
    }
    // issue #748
    String key = string.toLowerCase(Locale.ENGLISH);
    Class<T> value;
    if (typeAliases.containsKey(key)) {
      value = (Class<T>) typeAliases.get(key);
    } else {
      value = (Class<T>) Resources.classForName(string);
    }
    return value;
  } catch (ClassNotFoundException e) {
    throw new TypeException("Could not resolve type alias '" + string + "'.  Cause: " + e, e);
  }
}

3. typeAlias

3.1 registerAliases(typeAliasPackage)

  • TypeAliasRegistry 类
public void registerAliases(String packageName) {
  registerAliases(packageName, Object.class);
}
/* ResolverUtil: 处理工具类
 * 1. 生成一个 ResolverUtil 实例
 * 2. new ResolverUtil.IsA(superType): 生成一个 Test 实例, 
 *    find():将 packageName 包下与Test 匹配的 .class文件添加进 matches
 * 3. 获取 matches 为 typeSet,循环 class 类,若不为 匿名类&&接口&&成员类,添加进 typeAliases
 * */
public void registerAliases(String packageName, Class<?> superType) {
  ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();
  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);
    }
  }
  
}
  • ResolverUtil 类(部分属性和方法)
public class ResolverUtil<T> {
  private static final Log log = LogFactory.getLog(ResolverUtil.class);
  private Set<Class<? extends T>> matches = new HashSet<>();
  
  public interface Test {
    boolean matches(Class<?> type);
  }
  public static class IsA implements Test {
    private Class<?> parent;
    public IsA(Class<?> parentType) {
      this.parent = parentType;
    }
    /* 是否是相同的类或接口的 类对象表示,或是父类或超类或接口 */
    @Override
    public boolean matches(Class<?> type) {
      return type != null && parent.isAssignableFrom(type);
    }
    @Override
    public String toString() {
      return "is assignable to " + parent.getSimpleName();
    }
  }
  public ResolverUtil<T> find(Test test, String packageName) {
    String path = getPackagePath(packageName);
    try {
      List<String> children = VFS.getInstance().list(path);
      for (String child : children) {
        if (child.endsWith(".class")) {
          addIfMatching(test, child);
        }
      }
    } catch (IOException ioe) {
      log.error("Could not read package: " + packageName, ioe);
    }
    return this;
  }
  /* 包名 "." 换成 "/" */
  protected String getPackagePath(String packageName) {
    return packageName == null ? null : packageName.replace('.', '/');
  }
  /* 若类型匹配,将type 存储进 matches */
  @SuppressWarnings("unchecked")
  protected void addIfMatching(Test test, String fqn) {
    try {
      String externalName = fqn.substring(0, fqn.indexOf('.')).replace('/', '.');
      ClassLoader loader = getClassLoader();
      if (log.isDebugEnabled()) {
        log.debug("Checking to see if class " + externalName + " matches criteria [" + test + "]");
      }
      Class<?> type = loader.loadClass(externalName);
      if (test.matches(type)) {
        matches.add((Class<T>) type);
      }
    } catch (Throwable t) {
      log.warn("Could not examine class '" + fqn + "'" + " due to a " +
          t.getClass().getName() + " with message: " + t.getMessage());
    }
  }
  public Set<Class<? extends T>> getClasses() {
    return matches;
  }
}

3.2 registerAlias(Class<?> type)

  • 为 type 注册别名
  • TypeAliasRegistry 类
/* 若注册有设置别名,获取注册配置的别名 */
public void registerAlias(Class<?> type) {
  String alias = type.getSimpleName();
  Alias aliasAnnotation = type.getAnnotation(Alias.class);
  if (aliasAnnotation != null) {
    alias = aliasAnnotation.value();
  }
  registerAlias(alias, type);
}
/* 1.将别名转换为小写(即别名不区分大小写),
 * 2. 判断别名是否被注册(依据为存在别名key且Value不为传入的value)
 * 3. 添加别名进 typeAliases
 *  */
public void registerAlias(String alias, Class<?> value) {
  if (alias == null) {
    throw new TypeException("The parameter alias cannot be null");
  }
  // issue #748
  String key = alias.toLowerCase(Locale.ENGLISH);
  if (typeAliases.containsKey(key) && typeAliases.get(key) != null && !typeAliases.get(key).equals(value)) {
    throw new TypeException("The alias '" + alias + "' is already mapped to the value '" + typeAliases.get(key).getName() + "'.");
  }
  typeAliases.put(key, value);
}

4. plugin、objectFactory、objectWrapperFactory、reflectorFactoryElement

  • 这部分的函数在前面有体现过,newInstance():使用空构造函数创建一个实例

5. environments

5.1 isSpecifiedEnvironment(String id)

  • 判断当前 id 的值与指定的 environment 是否相同
private boolean isSpecifiedEnvironment(String id) {
  if (environment == null) {
    throw new BuilderException("No environment specified.");
  } else if (id == null) {
    throw new BuilderException("Environment requires an id attribute.");
  } else if (environment.equals(id)) {
    return true;
  }
  return false;
}

5.2 transactionManagerElement(XNode context)

  • 类似解析其他的节点,这里为解析 transactionManager 节点
/* 获取 type 指定的类型、property属性,处理type类型并创建实例 factory,设置属性值后返回 */
private TransactionFactory transactionManagerElement(XNode context) throws Exception {
  if (context != null) {
    String type = context.getStringAttribute("type");
    Properties props = context.getChildrenAsProperties();
    TransactionFactory factory = (TransactionFactory) resolveClass(type).newInstance();
    factory.setProperties(props);
    return factory;
  }
  throw new BuilderException("Environment declaration requires a TransactionFactory.");
}

5.3 dataSourceElement(XNode context)

  • 类似解析其他的节点,这里为解析 dataSource 节点
/* 获取 type 指定的类型、property属性,处理type类型并创建实例 factory,设置属性值后返回 */
private DataSourceFactory dataSourceElement(XNode context) throws Exception {
  if(context != null) {
    String type = context.getStringAttribute("type");
    Properties props = context.getChildrenAsProperties();
    DataSourceFactory factory = (DataSourceFactory) resolveClass(type).newInstance();
    factory.setProperties(props);
    return factory;
  }
  throw new BuilderException("Environment declaration requires a DataSourceFactory.");
}

6. databaseIdProvider

  • 用到的方法在前面都有提及,不再赘述。。

7. typeHandler

7.1 register(String packageName)

  • TypeHandlerRegistry 类
/* 与 Aliases 的注册类似,但这里的 matches 判断类为 TypeHandler.class */
public void register(String packageName) {
  ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();
  resolverUtil.find(new ResolverUtil.IsA(TypeHandler.class), packageName);
  Set<Class<? extends Class<?>>> handlerSet = resolverUtil.getClasses();
  for (Class<?> type : handlerSet) {
    /* 非匿名类 && 非接口 && 非抽象类(Modifier: 修饰符) */
    if (!type.isAnonymousClass() && !type.isInterface() && !Modifier.isAbstract(type.getModifiers())) {
      register(type); // 7.3.1 的重载方法
    }
  }
}

7.2 resolveJdbcType(String alias)

  • 通过名称获取 JdbcType
/* JdbcType: 枚举类 */
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);
  }
}

7.3 register()

  • 有 n 多个重载函数

7.3.1 register(Class<?> typeHandlerClass)

  • 若注解@MappedTypes 存在 value 值,调用 7.3.2 的重载函数
  • 若注解@MappedTypes 不存在 value 值,先生成 typeHandlerClass 的实例,再调用 XXX 的重载函数
public void register(Class<?> typeHandlerClass) {
  boolean mappedTypeFound = false;
  MappedTypes mappedTypes = typeHandlerClass.getAnnotation(MappedTypes.class);
  if (mappedTypes != null) {
    for (Class<?> javaTypeClass : mappedTypes.value()) {
      register(javaTypeClass, typeHandlerClass);
      mappedTypeFound = true;
    }
  }
  if (!mappedTypeFound) {
    register(getInstance(null, typeHandlerClass));
  }
}

7.3.2 register(Class<?> javaTypeClass, Class<?> typeHandlerClass)

  • 先生成 typeHandlerClass 的实例,再继续调用 7.3.3 的重载函数
public void register(Class<?> javaTypeClass, Class<?> typeHandlerClass) {
  register(javaTypeClass, getInstance(javaTypeClass, typeHandlerClass));
}

7.3.3 register(Class javaType, TypeHandler<? extends T> typeHandler)

  • 这里就做了一个 javaType 的强制转型,再继续调用 7.3.4 的重载函数
public <T> void register(Class<T> javaType, TypeHandler<? extends T> typeHandler) {
  register((Type) javaType, typeHandler);
}

7.3.4 register(Type javaType, TypeHandler<? extends T> typeHandler)

  • 若注解@MappedJdbcTypes 存在 value 值,调用 7.3.5 的重载函数,handledJdbcType 不为 null
  • 若注解@MappedJdbcTypes 不存在 value 值 或者 注解@MappedJdbcTypes 的value 值为 null,,调用 7.3.5 的重载函数,handledJdbcType 为 null
private <T> void register(Type javaType, TypeHandler<? extends T> typeHandler) {
  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);
  }
}

7.3.5 register(Type javaType, JdbcType jdbcType, TypeHandler<?> handler)

  • 首先来看 TypeHandlerRegistry 类的部分属性
public final class TypeHandlerRegistry {
  private final Map<JdbcType, TypeHandler<?>>  jdbcTypeHandlerMap = new EnumMap<>(JdbcType.class);
  private final Map<Type, Map<JdbcType, TypeHandler<?>>> typeHandlerMap = new ConcurrentHashMap<>();
  private final TypeHandler<Object> unknownTypeHandler = new UnknownTypeHandler(this);
  private final Map<Class<?>, TypeHandler<?>> allTypeHandlersMap = new HashMap<>();
  private static final Map<JdbcType, TypeHandler<?>> NULL_TYPE_HANDLER_MAP = Collections.emptyMap();
}  
  • 终点重载方法:将javaType、jdbcType、handler 存进 typeHandlerMap、allTypeHandlersMap
private void register(Type javaType, JdbcType jdbcType, TypeHandler<?> handler) {
  if (javaType != null) {
    Map<JdbcType, TypeHandler<?>> map = typeHandlerMap.get(javaType);
    if (map == null || map == NULL_TYPE_HANDLER_MAP) {
      map = new HashMap<>();
      typeHandlerMap.put(javaType, map);
    }
    map.put(jdbcType, handler);
  }
  allTypeHandlersMap.put(handler.getClass(), handler);
}

7.3.6 register(Class<?> javaTypeClass, JdbcType jdbcType, Class<?> typeHandlerClass)

  • 先生成 typeHandlerClass 的实例,再继续调用 7.3.7 的重载函数
public void register(Class<?> javaTypeClass, JdbcType jdbcType, Class<?> typeHandlerClass) {
  register(javaTypeClass, jdbcType, getInstance(javaTypeClass, typeHandlerClass));
}

7.3.7 register(Class type, JdbcType jdbcType, TypeHandler<? extends T> handler)

  • 先进行 type 的强制转型,再继续调用 7.3.5 的重载函数
public <T> void register(Class<T> type, JdbcType jdbcType, TypeHandler<? extends T> handler) {
  register((Type) type, jdbcType, handler);
}

7.4 getInstance()

  • 传入 类型处理器TypeHandler 与类型 javaType,生成一个管理 javaType 的处理器
/* 判断 javaType 是否为 null,判断使用 TypeHandler 的 无参/有参 构造器 */
@SuppressWarnings("unchecked")
public <T> TypeHandler<T> getInstance(Class<?> javaTypeClass, Class<?> typeHandlerClass) {
  if (javaTypeClass != null) {
    try {
      Constructor<?> c = typeHandlerClass.getConstructor(Class.class);
      return (TypeHandler<T>) c.newInstance(javaTypeClass);
    } catch (NoSuchMethodException ignored) {
      // ignored
    } catch (Exception e) {
      throw new TypeException("Failed invoking constructor for handler " + typeHandlerClass, e);
    }
  }
  try {
    Constructor<?> c = typeHandlerClass.getConstructor();
    return (TypeHandler<T>) c.newInstance();
  } catch (Exception e) {
    throw new TypeException("Unable to find a usable constructor for " + typeHandlerClass, e);
  }
}

8. mapper

8.1 addMappers(String packageName)

  • Configruation 类
public void addMappers(String packageName) {
  mapperRegistry.addMappers(packageName);
}
  • MapperRegistry
public void addMappers(String packageName) {
  addMappers(packageName, Object.class);
}
/* 将与 Object.class类 matches 匹配的 */
public void addMappers(String packageName, Class<?> superType) {
  ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();
  resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
  Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses();
  for (Class<?> mapperClass : mapperSet) {
    addMapper(mapperClass);
  }
}
  • 真正工作的 addMapper 方法
/* 若 type 为非接口,不做任何操作,也不报错
 * MapperProxyFactory: mapper 代理工厂,为创建 mapper 实例用
 * MapperAnnotationBuilder: 通过 type 的名称(UserMapper)加载 (UserMapper.)xml 文件, 并进行parse()解析
 * */
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.put(type, new MapperProxyFactory<>(type));
      MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
      parser.parse();
      loadCompleted = true;
    } finally {
      if (!loadCompleted) {
        knownMappers.remove(type);
      }
    }
  }
}

8.2 resource、url

  • 可以看到,resource、url 通过加载指定的 xml 文件,构造 XMLMapperBuilder 类,进行 parse() 解析,这不就类似 mybatis-config.xml。篇幅原因,新开一片讲解 Mapper.xml 的详细解析。

8.3 class

  • 通过 classForName(“classname”) 架子啊 class 指定的类型,直接调用添加 type 类型的方法,然后通过 type 名称查找 xml 文件并解析。
public <T> void addMapper(Class<T> type) {
  mapperRegistry.addMapper(type);
}
mybatis-config.xmlMyBatis的核心配置文件,用于配置MyBatis的全局信息和运行时行为。该文件包含了数据库连接信息和MyBatis所需的各种特性设置和属性。在实际开发中,我们通常会将数据库连接参数单独配置在db.properties文件中,然后在mybatis-config.xml中加载db.properties的属性值,这样就不需要在mybatis-config.xml中硬编码数据库连接参数。\[1\] mybatis-config.xml的结构包括: 1. configuration:MyBatis的配置信息 2. properties:可以配置在Java属性配置文件中的属性 3. settings:修改MyBatis在运行时的行为方式的设置 4. typeAliases:为Java类型命名一个别名(简称) 5. typeHandlers:类型处理器 6. objectFactory:对象工厂 7. plugins:插件 8. environments:环境配置 9. environment:环境变量 10. transactionManager:事务管理器 11. dataSource:数据源 12. mappers:映射器配置 在mybatis-config.xml中,可以使用<typeAliases>标签来定义别名。可以通过单个别名定义或批量别名定义来为Java类型命名别名。批量别名定义可以扫描指定包下的类,将类名作为别名(首字母大写或小写都可以)。\[3\] 总之,mybatis-config.xmlMyBatis的核心配置文件,用于配置全局信息和运行时行为,包含了数据库连接信息和各种特性设置和属性。可以通过配置文件来加载数据库连接参数,避免在配置文件中硬编码。同时,还可以使用<typeAliases>标签来定义Java类型的别名。 #### 引用[.reference_title] - *1* *3* [mybatis-config.xml文件的详情](https://blog.csdn.net/qq_60261230/article/details/126524763)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [Mybatis-config.xml配置文件基础配置详解](https://blog.csdn.net/qq_43795348/article/details/109553684)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值