解析过程分析如下:
private void pluginElement(XNode parent) throws Exception { if (parent != null) { for (XNode child : parent.getChildren()) { String interceptor = child.getStringAttribute("interceptor"); // 获取配置信息 Properties properties = child.getChildrenAsProperties(); // 解析拦截器的类型,并创建拦截器 Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance(); // 设置属性 interceptorInstance.setProperties(properties); // 添加拦截器到 Configuration 中 configuration.addInterceptor(interceptorInstance); } } }如上,插件解析的过程还是比较简单的。首先是获取配置,然后再解析拦截器类型,并实例化拦截器。最后向拦截器中设置属性,并将拦截器添加到 Configuration 中。好了,关于插件配置的分析就先到这,继续往下分析。
2.7 解析 environments 配置在 MyBatis 中,事务管理器和数据源是配置在 environments 中的。它们的配置大致如下:
<environments default="development"> <environment> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property value="${jdbc.driver}"/> <property value="${jdbc.url}"/> <property value="${jdbc.username}"/> <property value="${jdbc.password}"/> </dataSource> </environment> </environments>接下来我们对照上面的配置进行分析,如下:
private String environment; private void environmentsElement(XNode context) throws Exception { if (context != null) { if (environment == null) { // 获取 default 属性 environment = context.getStringAttribute("default"); } for (XNode child : context.getChildren()) { // 获取 id 属性 String id = child.getStringAttribute("id"); /* * 检测当前 environment 节点的 id 与其父节点 environments 的属性 default * 内容是否一致,一致则返回 true,否则返回 false */ if (isSpecifiedEnvironment(id)) { // 解析 transactionManager 节点,逻辑和插件的解析逻辑很相似,不在赘述 TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager")); // 解析 dataSource 节点,逻辑和插件的解析逻辑很相似,不在赘述 DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource")); // 创建 DataSource 对象 DataSource dataSource = dsFactory.getDataSource(); Environment.Builder environmentBuilder = new Environment.Builder(id) .transactionFactory(txFactory) .dataSource(dataSource); // 构建 Environment 对象,并设置到 configuration 中 configuration.setEnvironment(environmentBuilder.build()); } } } }environments 配置的解析过程没什么特别之处,按部就班解析就行了,不多说了。
2.8 解析 typeHandlers 配置在向数据库存储或读取数据时,我们需要将数据库字段类型和 Java 类型进行一个转换。比如数据库中有CHAR和VARCHAR等类型,但 Java 中没有这些类型,不过 Java 有String类型。所以我们在从数据库中读取 CHAR 和 VARCHAR 类型的数据时,就可以把它们转成 String 。在 MyBatis 中,数据库类型和 Java 类型之间的转换任务是委托给类型处理器TypeHandler去处理的。MyBatis 提供了一些常见类型的类型处理器,除此之外,我们还可以自定义类型处理器以非常见类型转换的需求。这里我就不演示自定义类型处理器的编写方法了,没用过或者不熟悉的同学可以 ,或者我在上一篇文章中写的示例。
下面,我们来看一下类型处理器的配置方法:
<!-- 自动扫描 --> <typeHandlers> <package/> </typeHandlers> <!-- 手动配置 --> <typeHandlers> <typeHandler jdbcType="TINYINT" javaType="xyz.coolblog.constant.ArticleTypeEnum" handler="xyz.coolblog.mybatis.ArticleTypeHandler"/> </typeHandlers>使用自动扫描的方式注册类型处理器时,应使用@MappedTypes和@MappedJdbcTypes注解配置javaType和jdbcType。关于注解,这里就不演示了,比较简单,大家自行尝试。下面开始分析代码。
private void typeHandlerElement(XNode parent) throws Exception { if (parent != null) { for (XNode child : parent.getChildren()) { // 从指定的包中注册 TypeHandler if ("package".equals(child.getName())) { String typeHandlerPackage = child.getStringAttribute("name"); // 注册方法 ① typeHandlerRegistry.register(typeHandlerPackage); // 从 typeHandler 节点中解析别名到类型的映射 } else { // 获取 javaType,jdbcType 和 handler 等属性值 String javaTypeName = child.getStringAttribute("javaType"); String jdbcTypeName = child.getStringAttribute("jdbcType"); String handlerTypeName = child.getStringAttribute("handler"); // 解析上面获取到的属性值 Class<?> javaTypeClass = resolveClass(javaTypeName); JdbcType jdbcType = resolveJdbcType(jdbcTypeName); Class<?> typeHandlerClass = resolveClass(handlerTypeName); // 根据 javaTypeClass 和 jdbcType 值的情况进行不同的注册策略 if (javaTypeClass != null) { if (jdbcType == null) { // 注册方法 ② typeHandlerRegistry.register(javaTypeClass, typeHandlerClass); } else { // 注册方法 ③ typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass); } } else { // 注册方法 ④ typeHandlerRegistry.register(typeHandlerClass); } } } } }