mybatis 配置加载详解

目录

介绍

加载mapper.xml的过程实际上就是一个填充 Configuration 对象数据的过程,如下图将xml里面的所有属性一一对应到类Configuration
映射脑图

本节介绍如何创建SqlSessionFactory,深度解析内部的内容

阅读本文需要熟悉jdk的xml解析相关内容

创建SqlSessionFactory

先来一段我们熟悉的加载SqlSessionFactory的代码

        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

这里我们第一步来分析如何创建的SqlSessionFactory

org.apache.ibatis.session.SqlSessionFactoryBuilder

//第一步
  public SqlSessionFactory build(InputStream inputStream) {
    return build(inputStream, null, null);
  }
//第二步,创建并填充Configuration
   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.
        }
      }
    }
    // 第三部 返回SqlSessionFactory
     public SqlSessionFactory build(Configuration config) {
        return new DefaultSqlSessionFactory(config);
      }

宏观的说: 我们已经解读了SqlSessionFactory的实现对象。 它的内部只有一个Configuration对象

创建Configuration

分析Configuration的创建过程就得分析XMLConfigBuilder xml配置构建器

org.apache.ibatis.session.SqlSessionFactoryBuilder

        XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);

分析构造 XMLConfigBuilder

org.apache.ibatis.builder.xml.XMLConfigBuilder

  public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
    this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
  }

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

这里就是解读XMLConfigBuilder有哪些字段
1. Configuration
2. 填充configuration的variables //new SqlSessionFactoryBuilder().build(inputStream,props);
3. 标记解析状态为未解析
4. 环境变量
5. xpath解析器

填充Configuration

org.apache.ibatis.session.SqlSessionFactoryBuilder

parser.parse()

org.apache.ibatis.builder.xml.XMLConfigBuilder

  public Configuration parse() {
    if (parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
  }
  1. 标记为已经解析
  2. parseConfiguration,填充Configuration对象的属性

parser.evalNode(“/configuration”) 的详细内容参见parser解析器XNode介绍

节点填充

 private void parseConfiguration(XNode root) {
    try {
      //issue #117 read properties first
      propertiesElement(root.evalNode("properties"));
      Properties settings = settingsAsProperties(root.evalNode("settings"));
      loadCustomVfs(settings);
      typeAliasesElement(root.evalNode("typeAliases"));
      pluginElement(root.evalNode("plugins"));
      objectFactoryElement(root.evalNode("objectFactory"));
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      reflectorFactoryElement(root.evalNode("reflectorFactory"));
      settingsElement(settings);
      // read it after objectFactory and objectWrapperFactory issue #631
      environmentsElement(root.evalNode("environments"));
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      typeHandlerElement(root.evalNode("typeHandlers"));
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }

这里就已经把mybatis.xml的根节点configuration的所有子节点填充到类Configuration里面了

节点填充解析

properties

org.apache.ibatis.builder.xml.XMLConfigBuilder

//propertiesElement(root.evalNode("properties"));
private void propertiesElement(XNode context) throws Exception {
    if (context != null) {
       // properties 节点的所有子节点
      Properties defaults = context.getChildrenAsProperties();
      String resource = context.getStringAttribute("resource");
      String url = context.getStringAttribute("url");
      // 同时存在url和resource,mybatis就蒙了
      if (resource != null && url != null) {
        throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference.  Please specify one or the other.");
      }
      //填充指定resource
      if (resource != null) {
        defaults.putAll(Resources.getResourceAsProperties(resource));
      } else if (url != null) {
      //填充指定url
        defaults.putAll(Resources.getUrlAsProperties(url));
      }
      // 在 new SqlSessionFactoryBuilder().build(inputStream,vars); 指定的vars
      Properties vars = configuration.getVariables();
      if (vars != null) {
        defaults.putAll(vars);
      }
      //刷新XPathParser的vars
      parser.setVariables(defaults);
      //刷新configuration的vars
      configuration.setVariables(defaults);
    }
  }

填充属性configuration.variables

typeAliases

org.apache.ibatis.builder.xml.XMLConfigBuilder

//typeAliasesElement(root.evalNode("typeAliases"));
 private void typeAliasesElement(XNode parent) {
    if (parent != null) {
    // 遍历 typeAliases 的所有子节点 
      for (XNode child : parent.getChildren()) {
        // 包扫描填充
        if ("package".equals(child.getName())) {
          String typeAliasPackage = child.getStringAttribute("name");
          configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);
        } else {

          String alias = child.getStringAttribute("alias");
          String type = child.getStringAttribute("type");
          try {
            Class<?> clazz = Resources.classForName(type);
            // 只有type时,从clazz获取Alias注解的value为别名  
            // <typeAlias type="com.aya.mapper.BlogMapper" />
            //@Alias("blogMapper")
            //public interface BlogMapper {}
            if (alias == null) {
              typeAliasRegistry.registerAlias(clazz);
            } else {
            // 直接指定别名 <typeAlias type="com.aya.mapper.BlogMapper" alias="blogMapper"/>
              typeAliasRegistry.registerAlias(alias, clazz);
            }
          } catch (ClassNotFoundException e) {
            throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);
          }
        }
      }
    }
  }

填充属性configuration.typeAliasRegistry

plugins

org.apache.ibatis.builder.xml.XMLConfigBuilder

   //pluginElement(root.evalNode("plugins"));
  private void pluginElement(XNode parent) throws Exception {
    if (parent != null) {
    // 遍历 plugins 子节点
      for (XNode child : parent.getChildren()) {
        String interceptor = child.getStringAttribute("interceptor");
        Properties properties = child.getChildrenAsProperties();
        // 创建属性 interceptor="com.aya.BlogInterceptor" 指定的类,必须实现接口Interceptor
        Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
        // 填充属性
        //   <plugins>
        //          <plugin interceptor="com.aya.MyInterceptor">
        //             <property name="name" value="value"/>
        //        </plugin>
        //   </plugins>
        interceptorInstance.setProperties(properties);
        configuration.addInterceptor(interceptorInstance);
      }
    }
  }

填充属性 configuration.interceptorChain

objectFactory|objectWrapperFactory|reflectorFactory

org.apache.ibatis.builder.xml.XMLConfigBuilder

//      objectFactoryElement(root.evalNode("objectFactory"));

 private void objectFactoryElement(XNode context) throws Exception {
    if (context != null) {
      String type = context.getStringAttribute("type");
      Properties properties = context.getChildrenAsProperties();
      ObjectFactory factory = (ObjectFactory) resolveClass(type).newInstance();
      factory.setProperties(properties);
      configuration.setObjectFactory(factory);
    }
  }
// objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
  private void objectWrapperFactoryElement(XNode context) throws Exception {
    if (context != null) {
      String type = context.getStringAttribute("type");
      ObjectWrapperFactory factory = (ObjectWrapperFactory) resolveClass(type).newInstance();
      configuration.setObjectWrapperFactory(factory);
    }
  }
//    reflectorFactoryElement(root.evalNode("reflectorFactory"));
  private void reflectorFactoryElement(XNode context) throws Exception {
    if (context != null) {
       String type = context.getStringAttribute("type");
       ReflectorFactory factory = (ReflectorFactory) resolveClass(type).newInstance();
       configuration.setReflectorFactory(factory);
    }
  }

上述代码都是一样的,创建对象并填充对应的属性

typeHandlers

org.apache.ibatis.builder.xml.XMLConfigBuilder

//      typeHandlerElement(root.evalNode("typeHandlers"));
private void typeHandlerElement(XNode parent) throws Exception {
    if (parent != null) {
    // 遍历typeHandlers子节点
      for (XNode child : parent.getChildren()) {
      //扫描包的所有类,注册类型处理器
        if ("package".equals(child.getName())) {
          String typeHandlerPackage = child.getStringAttribute("name");
          typeHandlerRegistry.register(typeHandlerPackage);
        } else {
        //单个指定注册
          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);
          // javaType 和 typeHandlerClass 必须都存在
          if (javaTypeClass != null) {
            if (jdbcType == null) {
            // xml指定了javaType,handler
              typeHandlerRegistry.register(javaTypeClass, typeHandlerClass);
            } else {
            // xml指定了javaType,jdbcType,handler
              typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass);
            }
          } else {
          // 获取类的注解 MappedTypes,作为javaType进行注册
            typeHandlerRegistry.register(typeHandlerClass);
          }
        }
      }
    }
  }

settings

//Properties settings = settingsAsProperties(root.evalNode("settings"));
  private Properties settingsAsProperties(XNode context) {
    if (context == null) {
      return new Properties();
    }
    //解析settings的所有子节点为Properties对象
    Properties props = context.getChildrenAsProperties();
    // Check that all settings are known to the configuration class
    //获取Configuration的元数据
    MetaClass metaConfig = MetaClass.forClass(Configuration.class, localReflectorFactory);
    for (Object key : props.keySet()) {
       //进行setter验证
       //    <settings>
       //          <setting name="useGeneratedKeys" value="false"/>  验证成功,configuration存在 setUseGeneratedKeys 方法
       //          <setting name="hahahaha" value="1"/>  验证失败,configuration存在 setHahahaha 方法
       //      </settings>
      if (!metaConfig.hasSetter(String.valueOf(key))) {
        throw new BuilderException("The setting " + key + " is not known.  Make sure you spelled it correctly (case sensitive).");
      }
    }
    return props;
  }
  1. settings的子节点转换为Properties
  2. 对Configuration的元数据验证

这里并没有对configuration进行填充

   //settingsElement(settings);
   private void settingsElement(Properties props) throws Exception {
       configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL")));
       configuration.setAutoMappingUnknownColumnBehavior(AutoMappingUnknownColumnBehavior.valueOf(props.getProperty("autoMappingUnknownColumnBehavior", "NONE")));
       configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true));
       configuration.setProxyFactory((ProxyFactory) createInstance(props.getProperty("proxyFactory")));
        // 省略填充
     }

settings 子节点的Properties对象填充到configuration

mappers

这里的内容太多,将单独详解mapper的填充

总结

  1. 创建SqlSessionFactory的过程就是一个将xml转换为Configuration对象的一个过程
  2. mybatis 封装了jdk的Node为XNode, 封装 xpath,document 为 XPathParser
  3. mybatis 封装了MetaClass,可以对setter,getter方法的存在进行判断

附录

parser解析器

org.apache.ibatis.builder.xml.XMLConfigBuilder

//SqlSessionFactoryBuilder 里面的 XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
    this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
  }

org.apache.ibatis.parsing.XPathParser


  public XPathParser(InputStream inputStream, boolean validation, Properties variables, EntityResolver entityResolver) {
    commonConstructor(validation, variables, entityResolver);
    // 载入文档
    this.document = createDocument(new InputSource(inputStream));
  }

    private void commonConstructor(boolean validation, Properties variables, EntityResolver entityResolver) {
      this.validation = validation;
      this.entityResolver = entityResolver;
      this.variables = variables;
      // 创建xpath
      XPathFactory factory = XPathFactory.newInstance();
      this.xpath = factory.newXPath();
    }
  1. 创建document,载入文档
  2. 创建xpath

XPathParser 就是根节点xml获取节点 的一个封装

xnode介绍

org.apache.ibatis.parsing.XNode

 public XNode(XPathParser xpathParser, Node node, Properties variables) {
 //parser解析器
    this.xpathParser = xpathParser;
    // 当前节点
    this.node = node;
    // 当前节点名词
    this.name = node.getNodeName();
    // 属性
    this.variables = variables;
    //解析节点的所有属性为Properties
    this.attributes = parseAttributes(node);
    //body 字符串
    this.body = parseBody(node);
  }

一个封装 Node 访问的一个类

元数据详解

org.apache.ibatis.builder.xml.XMLConfigBuilder

MetaClass.forClass(Configuration.class, localReflectorFactory);

将Class注册到元数据工厂,然后返回元数据。

元数据到底是什么呢?

// 创建元数据
  public static MetaClass forClass(Class<?> type, ReflectorFactory reflectorFactory) {
    return new MetaClass(type, reflectorFactory);
  }
  //注册中心获取 反射数据
 private MetaClass(Class<?> type, ReflectorFactory reflectorFactory) {
    this.reflectorFactory = reflectorFactory;
    this.reflector = reflectorFactory.findForClass(type);
  }

元数据包含了两个内容
1. 反射工厂
2. class的反射信息

反射工厂

org.apache.ibatis.reflection.ReflectorFactory

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

反射工厂 findForClass 用缓存的方式,缓存了Class对应的Reflector

反射信息

public Reflector(Class<?> clazz) {
    type = clazz;
    addDefaultConstructor(clazz);
    addGetMethods(clazz);
    addSetMethods(clazz);
    addFields(clazz);
    readablePropertyNames = getMethods.keySet().toArray(new String[getMethods.keySet().size()]);
    writeablePropertyNames = setMethods.keySet().toArray(new String[setMethods.keySet().size()]);
    for (String propName : readablePropertyNames) {
      caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
    }
    for (String propName : writeablePropertyNames) {
      caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
    }
  }

这里已经可以分析出反射信息的内容了
1. class 所有的 setter 方法
2. class 所有的 getter 方法
3. class 所有的 字段
4. class 所有的 构造
5. class 所有的 setter 参数类型
6. class 所有的 getter 返回值类型
7. class 所有的 setter 的忽略大小写
8. class 所有的 getter 的忽略大小写
有了这些内容就可以快速判断方法的存在性,类型一致性

猜你喜欢

转载自blog.csdn.net/mz4138/article/details/81307975