ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Mybatis:配置管理

2021-04-13 19:32:35  阅读:161  来源: 互联网

标签:typeAliasRegistry registerAlias 配置管理 private public Mybatis new class


查找配置文件

Resources

在使用Mybatis的过程中,通常使用MybatisResources类读取配置文件。在Mybatis 3.5.1API文档中,对Resources类的描述如下:

A class to simplify access to resources through the classloader.

Resources通过ClassLoader访问资源。
Resources类有两个静态属性:classLoaderWrapperCharset
ClassLoaderWrapper类是ClassLoader类的包装类。ClassLoaderWrapper类有两个属aaaaaaaa性defaultClassLoadersystemClassLoader分别存储默认的类加载器和系统类加载器。系统类加载器systemClassLoaderClassLoaderWrapper的构造方法中通过ClassLoader.getSystemClassLoader方法获取:

ClassLoaderWrapper() {
    try {
        systemClassLoader = ClassLoader.getSystemClassLoader();
    } catch (SecurityException ignored) {
        // AccessControlException on Google App Engine
    }
}

defaultClassLoader值则通过Resources类的静态方法setDefaultClassLoadergetDefaultClassLoader设置和获取。
ClassLoaderWrapper类中的方法有:

  • getResourceAsURL
  • getResourceAsStream
  • classForName
    每个方法都被重载,参数分别为资源路径;资源路径,类加载器;资源路径,类加载器数组。
    而前两种重载都调用了第三种重载,以getResourceAsURL为例:
public URL getResourceAsURL(String resource) {
    return getResourceAsURL(resource, getClassLoaders(null));
}
public URL getResourceAsURL(String resource, ClassLoader classLoader) {
    return getResourceAsURL(resource, getClassLoaders(classLoader));
}
URL getResourceAsURL(String resource, ClassLoader[] classLoader) {
    URL url;
    for (ClassLoader cl : classLoader) {
        if (null != cl) {
            // look for the resource as passed in...
            url = cl.getResource(resource);
            // ...but some class loaders want this leading "/", so we'll add it
            // and try again if we didn't find the resource
            if (null == url) {
                url = cl.getResource("/" + resource);
            }
            // "It's always in the last place I look for it!"
            // ... because only an idiot would keep looking for it after finding it, so stop looking already.
            if (null != url) {
                return url;
            }
        }
    }
    // didn't find it anywhere.
    return null;
}

getClassLoaders方法则规定了使用类加载器查找资源/类的顺序:

ClassLoader[] getClassLoaders(ClassLoader classLoader) {
    return new ClassLoader[]{
        classLoader,
        defaultClassLoader,
        Thread.currentThread().getContextClassLoader(),
        getClass().getClassLoader(),
        systemClassLoader};
}

读取配置文件

SqlSessionFactoryBuilder

SqlSessionFactoryBuilder创建SqlSessionFactory对象,类中所有方法全部为build方法及其重载。其中最重要的两个方法为:

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
        XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
        return build(parser.parse());
    } catch (Exception e) {
        throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
        ErrorContext.instance().reset();
        try {
            inputStream.close();
        } catch (IOException e) {
        // Intentionally ignore. Prefer previous error.
        }
    }
}

public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
}

可以看到builder方法首先创建了XMLConfigBuilder类的实例,接着使用该实例创建了DefaultSqlSessionFactory实例。

XMLConfigBuilder

XMLConfigBuilder类的构造方法都是如下构造方法的变形:

private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
    super(new Configuration());
    ErrorContext.instance().resource("SQL Mapper Configuration");
    this.configuration.setVariables(props);
    this.parsed = false;
    this.environment = environment;
    this.parser = parser;
}
public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
    this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
}

SqlSessionFactoryBuilder类调用的构造方法是第二个构造方法,由此可见传入的inputStram被处啊入到了XPathParser对象中,而XMLConfigBuilder类继承了BaseBuilder类。BaseBuilder类是抽象类,有三个属性:Configuration类对象configurationTypeAliasRegistry类对象typeAliasRegistryTypeHandlerRegistry类对象typeHandlerRegistry。其中configuration对象为默认构造方法创建,而typeAliasRegistry对象和typeHandlerRegistry对象泽分别由configuration对象的getTypeAliasRegistry()方法和getTypeHandlerRegistry()方法创建。

XPathParser

根据调用关系,XPathParser对象的构造方法的执行顺序为:

public XPathParser(InputStream inputStream) {
	commonConstructor(false, null, null);
	this.document = createDocument(new InputSource(inputStream));
}
private void commonConstructor(boolean validation, Properties variables, EntityResolver entityResolver) {
    this.validation = validation;
    this.entityResolver = entityResolver;
    this.variables = variables;
    XPathFactory factory = XPathFactory.newInstance();
    this.xpath = factory.newXPath();
}
private Document createDocument(InputSource inputSource) {
    // important: this must only be called AFTER common constructor
    try {
      DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
      factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
      factory.setValidating(validation);

      factory.setNamespaceAware(false);
      factory.setIgnoringComments(true);
      factory.setIgnoringElementContentWhitespace(false);
      factory.setCoalescing(false);
      factory.setExpandEntityReferences(true);

      DocumentBuilder builder = factory.newDocumentBuilder();
      builder.setEntityResolver(entityResolver);
      builder.setErrorHandler(new ErrorHandler() {
        @Override
        public void error(SAXParseException exception) throws SAXException {
          throw exception;
        }

        @Override
        public void fatalError(SAXParseException exception) throws SAXException {
          throw exception;
        }

        @Override
        public void warning(SAXParseException exception) throws SAXException {
          // NOP
        }
      });
      return builder.parse(inputSource);
    } catch (Exception e) {
      throw new BuilderException("Error creating document instance.  Cause: " + e, e);
    }
  }

可以看到在XPathParser的构造函数中就对配置进行了读取,对配置读取时使用的是DocumentBuilder创建Document对象。对XML文件读取完毕之后,XPathParser通过parseConfiguration方法读取配置文件中的配置。parseConfiguration方法以及其调用的方法将配置文件中的内容通过调用Configuration中的相应方法保存到Configuration对象中。

TypeAliasRegistry

TypeAliasRegistry类的主要功能是注册和查询别名。在TypeAliasRegistry.java中,TypeAliasRegistry类的属性有typeAliases这一HashMap对象,而TypeAliasRegistry类中的主要方法resolveAliasregisterAliasgetTypeAliases的行为则是对typeAliases进行操作。

private final Map<String, Class<?>> typeAliases = new HashMap<>();

typeAliases主要提供字符串和类的映射。方法registerAlias将一个名称和一个类相关联起来,getTypeAlias主要从HashMap中获取根据名称获取对应类的Class对象,而resolveAlias方法的源代码如下:

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

可以看到, resolveAlias方法对传入的字符串参数string,首先将其转换成全部小写,接着如果之前没有建立由改string到某一类的映射,则使用Resources.classForName方法获取string代表的类的Class对象并建立映射关系。例如,如果有一类名为org.glassyyang.User,则该方法获取org.glassyyang.User类对应的Class对象,接着建立org.glassyyang.userClass对象的映射关系。
分析registryAlias源码:

public void registerAliases(String packageName) {
    registerAliases(packageName, Object.class);
}
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) {
        // Ignore inner classes and interfaces (including package-info.java)
        // Skip also inner classes. See issue #6
        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);
}

可以看到方法在建立映射关系时,首先会将字符串转换成小写,这也是Mybatis的别名不区分大小写的原因。
TypeAliasRegistry的构造函数中,对Java中常见的基本类型,包装类型以及对应的数组类型都建立了映射关系;对DateBigDecimalBigIntegerObject以及对应数组对象也建立了映射关系,同时对MapHashMapListArrayListCollectionIteratorResultSet对象建立了映射关系。同时在Configuration对象的构造函数中创建了如下的对应关系:

public Configuration() {
    typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
    typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);
    typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
    typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
    typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);
    typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
    typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
    typeAliasRegistry.registerAlias("LRU", LruCache.class);
    typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
    typeAliasRegistry.registerAlias("WEAK", WeakCache.class);
    typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);
    typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class);
    typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class);
    typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
    typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);
    typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
    typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
    typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
    typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
    typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);
    typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class);
    typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class);
    
    languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
    languageRegistry.register(RawLanguageDriver.class);
}

TypeHandlerRegistry

TypeHandlerRegistry类的主要功能是提供Java类型与对应的Jdbc类型之间进行转换的处理句柄。
其中的属性jdbcTypeHandlerMap提供JdbcType与相应的处理句柄之间的映射关系,而属性typeHandlerMap是一个ConcurrentMap,提供java类型与Jdbc类型之间转换的处理句柄TypeHandler
TypeHandlerRegistry对象的属性如下:

// EnumMap,存储JdbcType枚举与TypeHandler<?>之间的映射关系,即保存了JdbcType的处理句柄
private final Map<JdbcType, TypeHandler<?>>  jdbcTypeHandlerMap = new EnumMap<>(JdbcType.class);
// ConcurrentHashMap对象,存储Type类与Map<JdbcTYpe, TYpeHandler<?>>之间的映射关系
private final Map<Type, Map<JdbcType, TypeHandler<?>>> typeHandlerMap = new ConcurrentHashMap<>();
// 特殊的TypeHandler,专门处理未知类型
private final TypeHandler<Object> unknownTypeHandler;
//定义类Class对象与TypeHandler之间的映射关系
private final Map<Class<?>, TypeHandler<?>> allTypeHandlersMap = new HashMap<>();
// 定义一个空的Map作为null值的Map
private static final Map<JdbcType, TypeHandler<?>> NULL_TYPE_HANDLER_MAP = Collections.emptyMap();
// 暂时不知道是用来干嘛的
private Class<? extends TypeHandler> defaultEnumTypeHandler = EnumTypeHandler.class;

TypeHandlerRegistry类的构造函数中注册了基本Java类型和JDBC类型相应的处理句柄。其中registry方法用于注册句柄,提供注册JdbcType对应处理句柄和Java类型转换为JdbcType类型的护理句柄。Mybatis中默认实现了一些jdbc类型和java类型之间转换的处理句柄,有BooleanTypeHandlerByteTypeHandlerShortTypeHandler等。这些Handler提供了jdbc类型和java类型的互相转换。

回到BaseBuilder。可以看到XMLConfigBuilder继承了BaseBuilder,并在其基础上新增了parsedparserenvironmentlocalReflectorFactory四个属性:

// 是否已解析,防止配置文件被解析多次;
private boolean parsed;
// XML 文件DOM解析包装类,用于解析XML文件
private final XPathParser parser;
//字符串类型,暂时不知道有神码用处
private String environment;
// 反射工厂类,不知道是干什么的
private final ReflectorFactory localReflectorFactory = new DefaultReflectorFactory();

标签:typeAliasRegistry,registerAlias,配置管理,private,public,Mybatis,new,class
来源: https://blog.csdn.net/qq_37718687/article/details/115553685

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有