MyBatis源码解析-configuration

在spring-mybatis.xml配置文件中,

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    	<property name="dataSource" ref="dataSource"/>
    	<property name="mapperLocations" value="classpath:com/aia/dao/impl/*.xml"/>
    <property name="plugins">
    <array>
      <bean class="com.github.pagehelper.PageInterceptor">
       <property name="properties">
                        <value></value>
        </property>
      </bean>
    </array>
  </property>
  </bean>

导入源码

public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
SqlSessionFactoryBean 实现了InitializingBean接口。要实现InitializingBean必须实现afterPropertiesSet方法。
afterPropertiesSet 初始化的时候执行,可以针对某个bean进行配置,就比如sqlSessionFactoryBean有afterPropertiesSet而其他bean不一定有。
执行顺序: 先afterPropertiesSet----initMethod---
如果有加后置处理器BeanPostProcessor,执行顺序 postProcessBeforeInitialization--afterPropertiesSet-initMethod--postProcessAfterInitialization
 /**
   * {@inheritDoc}
   */
  @Override
  public void afterPropertiesSet() throws Exception {
    notNull(dataSource, "Property 'dataSource' is required");
    notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
    state((configuration == null && configLocation == null) || !(configuration != null && configLocation != null),
              "Property 'configuration' and 'configLocation' can not specified with together");

    this.sqlSessionFactory = buildSqlSessionFactory();
  }

 继续跟踪,太长,先贴一段。

 protected SqlSessionFactory buildSqlSessionFactory() throws IOException {

    Configuration configuration;
    //这块同一个this.configuration !=null条件但是用了两个if-else if-,按实现功能来分开。
    XMLConfigBuilder xmlConfigBuilder = null;
    if (this.configuration != null) {
      configuration = this.configuration;
      if (configuration.getVariables() == null) {
        configuration.setVariables(this.configurationProperties);
      } else if (this.configurationProperties != null) {
        configuration.getVariables().putAll(this.configurationProperties);
      }
    } else if (this.configLocation != null) {
      //使用XMLConfigBuilder来解析。
      xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
      configuration = xmlConfigBuilder.getConfiguration();
    } else {
      LOGGER.debug(() -> "Property 'configuration' or 'configLocation' not specified, using default MyBatis Configuration");
      configuration = new Configuration();
      if (this.configurationProperties != null) {
        configuration.setVariables(this.configurationProperties);
      }
    }//未完待续---段落A

  跟踪XMLConfigBuilder,是调用了XPathParser。返回一个解析后的document对象。

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

    XPathParser是spring的核心包。是XPath的语法分析器。XPath主要作用就是用来解析xml文档。

    XpathParser中大部分都是构造器,还有eval开头的方法。如evalBoolean、evalFloat等。主要作用调用evaluate. evaluate语法.

构造器则都调用了commonConstructor,该方法会实例化一个xpath.

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

综上所述,这段代码就是:用xpath语法来解析xml文档,解析成一个document对象。

 
 
 /**
   * Code B
   * 对应的xml中的<property name='typeAliasesPackage' value='com.domain'>
   * 扫描对应的包,注册成一个hashmap.
   * hashmap的key-value : <首字母小写的类名,类的全路径>
   * 
   *
   */
 if (hasLength(this.typeAliasesPackage)) {
      String[] typeAliasPackageArray = tokenizeToStringArray(this.typeAliasesPackage,
          ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
      for (String packageToScan : typeAliasPackageArray) {
        configuration.getTypeAliasRegistry().registerAliases(packageToScan,
                typeAliasesSuperType == null ? Object.class : typeAliasesSuperType);
        LOGGER.debug(() -> "Scanned package: '" + packageToScan + "' for aliases");
      }
    }


    if (!isEmpty(this.typeAliases)) {
      for (Class<?> typeAlias : this.typeAliases) {
        configuration.getTypeAliasRegistry().registerAlias(typeAlias);
        LOGGER.debug(() -> "Registered type alias: '" + typeAlias + "'");
      }
    }未完待续,接Code C

    1.typeAliaserPackage是供扫描的Aliases的package。如下:

 /**
   * Packages to search for type aliases.
   *
   * @since 1.0.1
   *
   * @param typeAliasesPackage package to scan for domain objects
   *
   */
  public void setTypeAliasesPackage(String typeAliasesPackage) {
    this.typeAliasesPackage = typeAliasesPackage;
  }

2.tokenizeToStringArray的作用是 用分隔符将TypeAliasesPackage分成数组。

    3. 主要代码是

 configuration.getTypeAliasRegistry().registerAliases(packageToScan,
                typeAliasesSuperType == null ? Object.class : typeAliasesSuperType);

    大致作用是将扫描到的类,注册成一个Aliases,Aliases是别名。通过配置别名,我们不用再指定完整的包名,并且还能取别名    

跟踪进去

public void registerAliases(String packageName, Class<?> superType){
    ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>();
    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);
      }
    }

 AnonymousClass是匿名类,就是代码中没有明确指定的类,如直接new runnable接口,里面写实现方法。编译器会生成xxxx$1这样的class文件。

继续跟踪registerAlias方法。

  public void registerAlias(String alias, Class<?> value) {
    if (alias == null) {
      throw new TypeException("The parameter alias cannot be null");
    }
    // issue #748
    String key = alias.toLowerCase(Locale.ENGLISH);
    if (TYPE_ALIASES.containsKey(key) && TYPE_ALIASES.get(key) != null && !TYPE_ALIASES.get(key).equals(value)) {
      throw new TypeException("The alias '" + alias + "' is already mapped to the value '" + TYPE_ALIASES.get(key).getName() + "'.");
    }
    TYPE_ALIASES.put(key, value);
  }
其实是维护了一个 HashMap

private final Map<String, Class<?>> TYPE_ALIASES = new HashMap<String, Class<?>>();
 /**
   * Code C
   * 对应的xml中的<property name='plugins'>
   * 扫描对应的包,注册成一个hashmap.
   * hashmap的key-value : <首字母小写的类名,类的全路径>
   * 
   *
   */
if (!isEmpty(this.plugins)) { for (Interceptor plugin : this.plugins) { configuration.addInterceptor(plugin); LOGGER.debug(() -> "Registered plugin: '" + plugin + "'"); } }
 /**
   * Code D
   * 对应的xml中的<property name='mapperLocations' value='classpath:com.dao.impl/*.xml'>
   * 扫描对应的包,注册成一个hashmap.
   * 
   * 
   *
   */
if (!isEmpty(this.mapperLocations)) {
      for (Resource mapperLocation : this.mapperLocations) {
        if (mapperLocation == null) {
          continue;
        }

        try {
          XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
              configuration, mapperLocation.toString(), configuration.getSqlFragments());
          xmlMapperBuilder.parse();
        } catch (Exception e) {
          throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
        } finally {
          ErrorContext.instance().reset();
        }
        LOGGER.debug(() -> "Parsed mapper file: '" + mapperLocation + "'");
      }
    } else {
      LOGGER.debug(() -> "Property 'mapperLocations' was not specified or no matching resources found");
    }

1. 先循环遍历mapperLocations。    

2. 使用XMLMapperBuilder来解析包下的各个mapper xml文件。xmlMapperBuilder.parse()是解析mapper的方法。

跟踪parse方法。


resource是构造器中传入的configuration.getSqlFragments

跟踪下SqlFragements,发现是一个Map结构。StrictMap是单独写的一个实现类,增加了name属性,用来说明。

protected final Map<String, XNode> sqlFragments = new StrictMap<XNode>("XML fragments parsed from previous mappers");

如果configuration中没有加载过SqlFragement,加载过跳过。

跟踪进入configurationElement。


 先拿到namespace属性,

 再加载cache-ref,参照缓存是指原则上一个mapper分配一个cache对象,但是也可以设置cache-ref为多个Mapper来分配一个cache对象,

 再加载cache二级缓存

 再加载parameterMap入参

 再加载resultMap 返回结果集

 再加载sql。sql是将共同的select field 提出来,避免重复代码。

 再加载select、insert、update、delete

1.分析加载cache-ref。

维护了一个chcheRefMap,将当前的namespace跟想ref去的namespace关联起来。 

private void cacheRefElement(XNode context) {
    if (context != null) {
      configuration.addCacheRef(builderAssistant.getCurrentNamespace(), context.getStringAttribute("namespace"));
      CacheRefResolver cacheRefResolver = new CacheRefResolver(builderAssistant, context.getStringAttribute("namespace"));
      try {
        cacheRefResolver.resolveCacheRef();
      } catch (IncompleteElementException e) {
        configuration.addIncompleteCacheRef(cacheRefResolver);
      }
    }
  }

跟踪津resolveCacheRef。具体实现如下。用boolean变量来防止出错。

 public Cache useCacheRef(String namespace) {
    if (namespace == null) {
      throw new BuilderException("cache-ref element requires a namespace attribute.");
    }
    try {
      unresolvedCacheRef = true;
      Cache cache = configuration.getCache(namespace);
      if (cache == null) {
        throw new IncompleteElementException("No cache for namespace '" + namespace + "' could be found.");
      }
      currentCache = cache; //起作用的code
      unresolvedCacheRef = false;
      return cache;
    } catch (IllegalArgumentException e) {
      throw new IncompleteElementException("No cache for namespace '" + namespace + "' could be found.", e);
    }
  }
   2 .分析加载cache。
private void cacheElement(XNode context) throws Exception {
    if (context != null) {
      String type = context.getStringAttribute("type", "PERPETUAL");
      Class<? extends Cache> typeClass = typeAliasRegistry.resolveAlias(type);
      String eviction = context.getStringAttribute("eviction", "LRU");//回收机制,如未设值,默认LRU最近最少使用
      Class<? extends Cache> evictionClass = typeAliasRegistry.resolveAlias(eviction);
      Long flushInterval = context.getLongAttribute("flushInterval");
      Integer size = context.getIntAttribute("size");
      boolean readWrite = !context.getBooleanAttribute("readOnly", false);
      boolean blocking = context.getBooleanAttribute("blocking", false);
      Properties props = context.getChildrenAsProperties();
      builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWrite, blocking, props);
    }
  }

 cache有一个实现类PerpetualCache.本质是一个hashMap。


 3.分析加载parameterMap。

private void parameterMapElement(List<XNode> list) throws Exception {
    for (XNode parameterMapNode : list) {
      String id = parameterMapNode.getStringAttribute("id");
      String type = parameterMapNode.getStringAttribute("type");
      Class<?> parameterClass = resolveClass(type);
      List<XNode> parameterNodes = parameterMapNode.evalNodes("parameter");
      List<ParameterMapping> parameterMappings = new ArrayList<ParameterMapping>();
      for (XNode parameterNode : parameterNodes) {
        String property = parameterNode.getStringAttribute("property");
        String javaType = parameterNode.getStringAttribute("javaType");
        String jdbcType = parameterNode.getStringAttribute("jdbcType");
        String resultMap = parameterNode.getStringAttribute("resultMap");
        String mode = parameterNode.getStringAttribute("mode");
        String typeHandler = parameterNode.getStringAttribute("typeHandler");
        Integer numericScale = parameterNode.getIntAttribute("numericScale");
        ParameterMode modeEnum = resolveParameterMode(mode);
        Class<?> javaTypeClass = resolveClass(javaType);
        JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType);
        @SuppressWarnings("unchecked")
        Class<? extends TypeHandler<?>> typeHandlerClass = (Class<? extends TypeHandler<?>>) resolveClass(typeHandler);
        ParameterMapping parameterMapping = builderAssistant.buildParameterMapping(parameterClass, property, javaTypeClass, jdbcTypeEnum, resultMap, modeEnum, typeHandlerClass, numericScale);
        parameterMappings.add(parameterMapping);
      }
      builderAssistant.addParameterMap(id, parameterClass, parameterMappings);
    }



猜你喜欢

转载自blog.csdn.net/Damon__Wang/article/details/80291487