我们通过上面源码剖析总结: 1.解析节点:typeAliases的子元素解析顺序可以说是先package后typeAlias; 2.typeAlias中alias别名可以配置或者不配置,两者区别是什么,接下来我们将继续分析; 3.不管是package还是typeAlias元素节点配置,最终均注册到mybatis的全局配置类Configuration中的TypeAliasRegistry容器中;
package元素源码解析过程 通过源码解析分析上面我们知道typeAliases节点中元素package,最终获取到的属性name中的包路径,然后调用mybatis的全局配置类Configuration中的TypeAliasRegistry容器类的 registerAliases(String packageName)方法进行注册。接下来我们顺着源码的调用看看package元素中的包路径是怎么注册到TypeAliasRegistry容器中的; public class TypeAliasRegistry { private final Map<String, Class<?>> typeAliases = new HashMap<>(); public void registerAliases(String packageName) { //实际调用的是registerAliases(String packageName, Class<?> superType) 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); } public void registerAlias(String alias, Class<?> value) { if (alias == null) { throw new TypeException("The parameter alias cannot be null"); } // 把解析完后的包路径下的bean的首字母进行小写 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() + "'."); } // 最终解析完之后包路径下的所有类的别名作为key,类的Class对象作为Value保存在TypeAliasRegistry容器中 //注意:typeAliases为一个HashMap集合 typeAliases.put(key, value); } }上面的源码调用链路比较长,这里就不具体的一一解释,我们重点落在两个地方: 1.解析完后的包路径下的bean的首字母进行小写; 2.包路径下的所有类的别名作为key,类的Class对象作为Value保存在typeAliases中 总结:package元素解析,就是扫描包下的所有bean,然后默认别名为类名的首字母小写,最终注册到全局配置类Configuration中的TypeAliasRegistry容器类中,容器中实际使用的是HashMap来存储:bean的首字母小写为作为key,bean的Class对象为Value。
typeAlias元素源码解析过程 public class TypeAliasRegistry { public void registerAlias(Class<?> type) { //获取类的简写名称(不包含包路径) String alias = type.getSimpleName(); //如果类上面有注解@Alias则注解中的值作为类别名,否则类名默认为类的简写名称 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); 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() + "'."); } // 最终解析完之后typeAlias中配置的类别名作为key,类的Class对象作为Value保存在TypeAliasRegistry容器中 typeAliases.put(key, value); } }总结: 1.typeAlias中的属性type进行对bean反射获取它的Class对象 2.属性alias没配置,就使用bean本身的类的简写名称(不包含包路径),如果bean的类上面有注解@Alias(""),则以注解中的值作为类别名,别名都默认进行首字母小写。 3.最终注册到全局配置类Configuration中的TypeAliasRegistry容器类中,bean的别名首字母小写为作为key,bean的Class对象为Value; 4.源码分析到这里,我们解决了一开始提出的问题2:解析节点元素package和typeAlias顺序和方式是什么?那么问题1:如果不配置节点typeAliases的时候,mybatis自己会做些什么呢?接下来我们就开始寻找问题的答案;
无节点typeAliases配置源码分析 首先我们来回顾XMLConfigBuilder实例化时父类BaseBuilder中的全局配置类Configuration初始化的地方 public class XMLConfigBuilder extends BaseBuilder { private XMLConfigBuilder(XPathParser parser, String environment, Properties props) { //初始化mybatis的全局配置类,调用的是无参构造方法 super(new Configuration()); ErrorContext.instance().resource("SQL Mapper Configuration"); this.configuration.setVariables(props); this.parsed = false; this.environment = environment; this.parser = parser; } } Configuration的无参构造方法 public class Configuration { protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry(); 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); } }看到这里我们知道了,mybatis在初始化全局配置类Configuration的时候在其无参构造方法,自动注册了一堆bean别名和他们的Class对象到typeAliasRegistry容器类中,而节点typeAliases配置的别名最终也是注册到typeAliasRegistry容器类中,为了方便后面直接获取调用; 当然事情还没结束,我们接着来窥探typeAliasRegistry类别名注册容器的内部。
public class TypeAliasRegistry { private final Map<String, Class<?>> typeAliases = new HashMap<>(); public TypeAliasRegistry() { registerAlias("string", String.class); registerAlias("byte", Byte.class); registerAlias("long", Long.class); registerAlias("short", Short.class); registerAlias("int", Integer.class); registerAlias("integer", Integer.class); registerAlias("double", Double.class); registerAlias("float", Float.class); registerAlias("boolean", Boolean.class); registerAlias("byte[]", Byte[].class); registerAlias("long[]", Long[].class); registerAlias("short[]", Short[].class); registerAlias("int[]", Integer[].class); registerAlias("integer[]", Integer[].class); registerAlias("double[]", Double[].class); registerAlias("float[]", Float[].class); registerAlias("boolean[]", Boolean[].class); registerAlias("_byte", byte.class); registerAlias("_long", long.class); registerAlias("_short", short.class); registerAlias("_int", int.class); registerAlias("_integer", int.class); registerAlias("_double", double.class); registerAlias("_float", float.class); registerAlias("_boolean", boolean.class); registerAlias("_byte[]", byte[].class); registerAlias("_long[]", long[].class); registerAlias("_short[]", short[].class); registerAlias("_int[]", int[].class); registerAlias("_integer[]", int[].class); registerAlias("_double[]", double[].class); registerAlias("_float[]", float[].class); registerAlias("_boolean[]", boolean[].class); registerAlias("date", Date.class); registerAlias("decimal", BigDecimal.class); registerAlias("bigdecimal", BigDecimal.class); registerAlias("biginteger", BigInteger.class); registerAlias("object", Object.class); registerAlias("date[]", Date[].class); registerAlias("decimal[]", BigDecimal[].class); registerAlias("bigdecimal[]", BigDecimal[].class); registerAlias("biginteger[]", BigInteger[].class); registerAlias("object[]", Object[].class); registerAlias("map", Map.class); registerAlias("hashmap", HashMap.class); registerAlias("list", List.class); registerAlias("arraylist", ArrayList.class); registerAlias("collection", Collection.class); registerAlias("iterator", Iterator.class); registerAlias("ResultSet", ResultSet.class); } }看到没有,TypeAliasRegistry 本身在初始化的时候,也会自动注册一些bean的别名和Class对象到自身最终存储HashMap中去,而这些大多是我们常常用到的基本类型的包装类和数组,字符串,集合等等,所以就算我们做相关的节点typeAliases配置,mybatis通过全局配置类Configuration,以其属性中的类别名注册容器TypeAliasRegistry为我们提前准备好了很多实用和常用的类别名,方便我们后期的调用。
节点typeAliases配置的好处 上面对mybatis-config.xml配置文件节点typeAliases在源码中的解析分析了半天,我们接下来就来讲讲节点typeAliases配置的好处是什么? 好处就是,让我们mapper等映射文件中写sql的时候,不需要把类的完全路径全部写出来,直接写别名,mybatis通过类别名注册容器TypeAliasRegistry可以自动帮我们找到Class对象,通过反射进行一系列的操作,使用上更方便; 还有就是提前注册好了mybatis常常需要使用的一些类别名和他们的Class对象,不需要等用的时候在进行加载