Mybatis:配置管理

查找配置文件

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

DefaultReflectorFactory

DefaultReflectorFactory实现了接口ReflectorFactory,内部有两个属性:

private boolean classCacheEnabled = true;
private final ConcurrentMap<Class<?>, Reflector> reflectorMap = new ConcurrentHashMap<>();

最重要的方法有:

@Override
public Reflector findForClass(Class<?> type) {
    if (classCacheEnabled) {
        // synchronized (type) removed see issue #461
        return reflectorMap.computeIfAbsent(type, Reflector::new);
    } else {
        return new Reflector(type);
    }
}

可以看到DefaultReflectorFactory是一个实现了缓存的保存typeReflector见映射关系的线程安全的类。

Reflector

Reflector的构造方法为:

// 添加clazz中的所有Getter方法到创建的Map conflictingGetters中;
private void addGetMethods(Class<?> clazz) {
    Map<String, List<Method>> conflictingGetters = new HashMap<>();
    Method[] methods = getClassMethods(clazz);
    // 将数组转换成流
    Arrays.stream(methods)
    // 筛选数组中没有参数且是Getter方法的method对象
    .filter(m -> m.getParameterTypes().length == 0 && PropertyNamer.isGetter(m.getName()))
    // 遍历每一个method对象,对于每一个getter,将其添加到map中;
    .forEach(m -> addMethodConflict(conflictingGetters, PropertyNamer.methodToProperty(m.getName()), m));
    // 处理map中的冲突。
    resolveGetterConflicts(conflictingGetters);
}
// 方法将method对象添加到map conflictingMethods中;
private void addMethodConflict(Map<String, List<Method>> conflictingMethods, String name, Method method) {
    if (isValidPropertyName(name)) {
        List<Method> list = conflictingMethods.computeIfAbsent(name, k -> new ArrayList<>());
        list.add(method);
    }
}
// 处理getters中的冲突:
private void resolveGetterConflicts(Map<String, List<Method>> conflictingGetters) {
    for (Entry<String, List<Method>> entry : conflictingGetters.entrySet()) {
        // 最终选择的method对象;
        Method winner = null;
        String propName = entry.getKey();
        boolean isAmbiguous = false;
        for (Method candidate : entry.getValue()) {
	        if (winner == null) {
	            winner = candidate;
	            continue;
	        }
	        // 获取winner的返回类型
	        Class<?> winnerType = winner.getReturnType();
	        // 获取竞选者的返回值类型
	        Class<?> candidateType = candidate.getReturnType();
	        // 返回类型是否相同
	        if (candidateType.equals(winnerType)) {
	            if (!boolean.class.equals(candidateType)) {
	            isAmbiguous = true;
	            break;
	            } else if (candidate.getName().startsWith("is")) {
	            winner = candidate;
	            }
	        // 获胜者返回类型是否是竞选者返回类型的子类
	        } else if (candidateType.isAssignableFrom(winnerType)) {
	            // OK getter type is descendant
	        // 竞选者返回类型是否是获胜者返回类型的子类
	        } else if (winnerType.isAssignableFrom(candidateType)) {
	            winner = candidate;
	        } else {
	        	// 出现了一个属性的两个getter,他们的返回类型不能互相转换;直接结束循环
	            isAmbiguous = true;
	            break;
	        }
        }
        // 添加getter method
        addGetMethod(propName, winner, isAmbiguous);
    }
}
// 功能:为传入的Method对象创建MethodInvoker对象,如果是冲突的,则创建AmbiguousMethodInvoker对象;否则创建MethodInvoker对象。
private void addGetMethod(String name, Method method, boolean isAmbiguous) {
    MethodInvoker invoker = isAmbiguous
        ? new AmbiguousMethodInvoker(method, MessageFormat.format(
            "Illegal overloaded getter method with ambiguous type for property ''{0}'' in class ''{1}''. This breaks the JavaBeans specification and can cause unpredictable results.",
            name, method.getDeclaringClass().getName()))
        : new MethodInvoker(method);
    // 将创建的MethodInvoker添加到getMethods对象中;
    getMethods.put(name, invoker);
    Type returnType = TypeParameterResolver.resolveReturnType(method, type);
    // 将Method对象的返回值添加到getTypes中;
    getTypes.put(name, typeToClass(returnType));
}
private void addSetMethods(Class<?> clazz) {
    Map<String, List<Method>> conflictingSetters = new HashMap<>();
    Method[] methods = getClassMethods(clazz);
    Arrays.stream(methods).filter(m -> m.getParameterTypes().length == 1 && PropertyNamer.isSetter(m.getName()))
        .forEach(m -> addMethodConflict(conflictingSetters, PropertyNamer.methodToProperty(m.getName()), m));
    resolveSetterConflicts(conflictingSetters);
}
private void resolveSetterConflicts(Map<String, List<Method>> conflictingSetters) {
    for (Entry<String, List<Method>> entry : conflictingSetters.entrySet()) {
        String propName = entry.getKey();
        List<Method> setters = entry.getValue();
        Class<?> getterType = getTypes.get(propName);
        boolean isGetterAmbiguous = getMethods.get(propName) instanceof AmbiguousMethodInvoker;
        boolean isSetterAmbiguous = false;
        Method match = null;
        for (Method setter : setters) {
        if (!isGetterAmbiguous && setter.getParameterTypes()[0].equals(getterType)) {
            // should be the best match
            match = setter;
            break;
        }
        if (!isSetterAmbiguous) {
            match = pickBetterSetter(match, setter, propName);
            isSetterAmbiguous = match == null;
        }
        }
        if (match != null) {
        addSetMethod(propName, match);
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值