美文网首页
Mybatis系列之MapperRegister

Mybatis系列之MapperRegister

作者: 超人也害羞 | 来源:发表于2019-07-16 00:25 被阅读0次

    一、思路

    1. MapperRegister初始化过程;
    2. MapperRegister使用过程;

    二、MapperRegister初始化过程

    我们依然以Mybatis结合Spring的背景来分析MapperRegister。首先回顾一下之前SqlSessionFactory初始化过程,我们分析mybats-spring-1.2.5-sources.jar包中初始化代码。

    protected SqlSessionFactory buildSqlSessionFactory() throws IOException {
      //  ...省略部分代码
      if (xmlConfigBuilder != null) {
          try {
            xmlConfigBuilder.parse();
    
            if (LOGGER.isDebugEnabled()) {
              LOGGER.debug("Parsed configuration file: '" + this.configLocation + "'");
            }
          } catch (Exception ex) {
            throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex);
          } finally {
            ErrorContext.instance().reset();
          }
        }
        //  ...省略部分代码    
        return this.sqlSessionFactoryBuilder.build(configuration);
      }
    

    关注其中的xmlConfigBuilder.parse();这里就开始对XX-Mapper.XML的内容进行解析,往下走。

    private void parseConfiguration(XNode root) {
        try {
          propertiesElement(root.evalNode("properties")); //issue #117 read properties first
          typeAliasesElement(root.evalNode("typeAliases"));
          pluginElement(root.evalNode("plugins"));
          objectFactoryElement(root.evalNode("objectFactory"));
          objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
          settingsElement(root.evalNode("settings"));
          environmentsElement(root.evalNode("environments")); // read it after objectFactory and objectWrapperFactory issue #631
          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);
        }
      }
    

    这里就是解析整个XML Config文件,其中的mapperElement(root.evalNode("mappers"));就是对我们定义的XX-Mapper.XML进行解析;

    private void mapperElement(XNode parent) throws Exception {
        if (parent != null) {
          for (XNode child : parent.getChildren()) {
            if ("package".equals(child.getName())) {
              String mapperPackage = child.getStringAttribute("name");
              configuration.addMappers(mapperPackage);
            } else {
              String resource = child.getStringAttribute("resource");
              String url = child.getStringAttribute("url");
              String mapperClass = child.getStringAttribute("class");
              if (resource != null && url == null && mapperClass == null) {
                ErrorContext.instance().resource(resource);
                InputStream inputStream = Resources.getResourceAsStream(resource);
                XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
                mapperParser.parse();
              } else if (resource == null && url != null && mapperClass == null) {
                ErrorContext.instance().resource(url);
                InputStream inputStream = Resources.getUrlAsStream(url);
                XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
                mapperParser.parse();
              } else if (resource == null && url == null && mapperClass != null) {
                Class<?> mapperInterface = Resources.classForName(mapperClass);
                configuration.addMapper(mapperInterface);
              } else {
                throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
              }
            }
          }
        }
      }
    

    解析出接口名后就将该接口注册到configuration中的MapperRegister中,configuration.addMappers(mapperPackage),最终还是调用了MapperRegister中的AddMapper(...)方法。
    至此,MapperRegister的初始化流程就简单介绍到这里,下面我们看一下MapperRegister的使用。


    三、MapperRegister使用过程

    MapperRegister使用过程主要是生成对应MapperProxy实例交给Spring进行管理。我们以一个Spring获取Bean的例子来讲解MapperRegister在其中起到了怎样的作用。

    这段代码中,我们主要是解析了spring配置文件后,使用ApplicationContext来获取PersonMapper这个bean;

    ApplicationContext context = new FileSystemXmlApplicationContext("classpath:spring.xml");
    PersonMapper mapper = (PersonMapper) context.getBean("personMapper");
    

    一路debug跟踪下去,我们会发现最终是调用MapperFactoryBean这个对象。(这里就忽略spring的过程了,关注点在mybatis上)

    /**
       * {@inheritDoc}
       */
      @Override
      public T getObject() throws Exception {
        return getSqlSession().getMapper(this.mapperInterface);
      }
    

    继续往下,spring使用的SqlSession是SqlSessionTemplate,我们再看看SqlSessionTemplate中是怎么实现的。

    /**
       * {@inheritDoc}
       */
      @Override
      public <T> T getMapper(Class<T> type) {
        return getConfiguration().getMapper(type, this);
      }
    

    发现最终还是调用的Configuration的getMapper方法。

    public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
        return mapperRegistry.getMapper(type, sqlSession);
      }
    

    继续看,我们发现最终还是调用了MapperRegister自己的getMapper方法。

    public class MapperRegistry {
    
      private Configuration config;
      // 之前xmlConfigBuilder.parse()解析完成注册的对象。
      private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<Class<?>, MapperProxyFactory<?>>();
    
      public MapperRegistry(Configuration config) {
        this.config = config;
      }
    
      @SuppressWarnings("unchecked")
      public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
        final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
        if (mapperProxyFactory == null)
          throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
        try {
          // 使用jdk的动态代理方法生成MapperProxy代理对象。
          return mapperProxyFactory.newInstance(sqlSession);
        } catch (Exception e) {
          throw new BindingException("Error getting mapper instance. Cause: " + e, e);
        }
      }
      // ... 省略部分代码
    }
    

    最终我们看到了还是调用了JDK自身的动态代理方法来生成一个MapperProxy代理对象。


    好了,今天就到这了~~晚安

    相关文章

      网友评论

          本文标题:Mybatis系列之MapperRegister

          本文链接:https://www.haomeiwen.com/subject/lceakctx.html