SEARU.ORG
当前位置:SEARU.ORG > Linux 新闻 > 正文

MyBatis解析自身配置文件记录

待日后有所提高之后在进行修正更新

参考:
+ mybatis官方文档
+ ApacheDerby WIKI
+ Apache Derby 项目资源

    
    public class SqlSessionTest extends BaseDataTest {
      private static SqlSessionFactory sqlMapper;
    
      @BeforeClass
      public static void setup() throws Exception {
        createBlogDataSource();
        final String resource = "org/apache/ibatis/builder/MapperConfig.xml";
        final Reader reader = Resources.getResourceAsReader(resource);
        sqlMapper = new SqlSessionFactoryBuilder().build(reader);
      }

从下面的解析可以看出mybatis最主要的一个配置存储对象是Configuration,我们在使用的时候可以不时的看看,我们配的东西和在使用的时候取的东西,会不会是在该对象里面了,以便我们更好的理解其底层的操作:
下面这个链接可以参考一下:
深入浅出MyBatis-Configuration

package org.apache.ibatis.session; 
/**
 * @author Clinton Begin
 */
public class Configuration {

  protected Environment environment;

  protected boolean safeRowBoundsEnabled = false;
  protected boolean safeResultHandlerEnabled = true;
  protected boolean mapUnderscoreToCamelCase = false;
  protected boolean aggressiveLazyLoading = true;
  protected boolean multipleResultSetsEnabled = true;
  protected boolean useGeneratedKeys = false;
  protected boolean useColumnLabel = true;
  protected boolean cacheEnabled = true;
  protected boolean callSettersOnNulls = false;

  protected String logPrefix;
  protected Class <? extends Log> logImpl;
  protected LocalCacheScope localCacheScope = LocalCacheScope.SESSION;
  protected JdbcType jdbcTypeForNull = JdbcType.OTHER;
  protected Set<String> lazyLoadTriggerMethods = new HashSet<String>(Arrays.asList(new String[] { "equals", "clone", "hashCode", "toString" }));
  protected Integer defaultStatementTimeout;
  protected Integer defaultFetchSize;
  protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;
  protected AutoMappingBehavior autoMappingBehavior = AutoMappingBehavior.PARTIAL;

  protected Properties variables = new Properties();
  protected ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
  protected ObjectFactory objectFactory = new DefaultObjectFactory();
  protected ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory();
  protected MapperRegistry mapperRegistry = new MapperRegistry(this);

  protected boolean lazyLoadingEnabled = false;
  protected ProxyFactory proxyFactory = new JavassistProxyFactory(); // #224 Using internal Javassist instead of OGNL

  protected String databaseId;
  /**
   * Configuration factory class.
   * Used to create Configuration for loading deserialized unread properties.
   *
   * @see <a href='https://code.google.com/p/mybatis/issues/detail?id=300'>Issue 300</a> (google code)
   */
  protected Class<?> configurationFactory;

  protected final InterceptorChain interceptorChain = new InterceptorChain();//缓存系统注册的拦截器查询,内部通过一个private final List<Interceptor> interceptors = new ArrayList<Interceptor>();来存储,Interceptor接口即插件模型
  protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry();
  protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
  protected final LanguageDriverRegistry languageRegistry = new LanguageDriverRegistry();

  protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection");
  protected final Map<String, Cache> caches = new StrictMap<Cache>("Caches collection");
  protected final Map<String, ResultMap> resultMaps = new StrictMap<ResultMap>("Result Maps collection");
  protected final Map<String, ParameterMap> parameterMaps = new StrictMap<ParameterMap>("Parameter Maps collection");
  protected final Map<String, KeyGenerator> keyGenerators = new StrictMap<KeyGenerator>("Key Generators collection");

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

  protected final Collection<XMLStatementBuilder> incompleteStatements = new LinkedList<XMLStatementBuilder>();
  protected final Collection<CacheRefResolver> incompleteCacheRefs = new LinkedList<CacheRefResolver>();
  protected final Collection<ResultMapResolver> incompleteResultMaps = new LinkedList<ResultMapResolver>();
  protected final Collection<MethodResolver> incompleteMethods = new LinkedList<MethodResolver>();

  /*
   * A map holds cache-ref relationship. The key is the namespace that
   * references a cache bound to another namespace and the value is the
   * namespace which the actual cache is bound to.
   */
  protected final Map<String, String> cacheRefMap = new HashMap<String, String>();

  public Configuration(Environment environment) {
    this();
    this.environment = environment;
  }

  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);
  }
  //略
  • createBlogDataSource();

         public static DataSource createBlogDataSource() throws IOException, SQLException {
        DataSource ds = createUnpooledDataSource(BLOG_PROPERTIES);
        runScript(ds, BLOG_DDL);
        runScript(ds, BLOG_DATA);
        return ds;
      }
    
    • DataSource ds = createUnpooledDataSource(BLOG_PROPERTIES);
        public static UnpooledDataSource createUnpooledDataSource(String resource) throws IOException {
    /*
    读取public static final String BLOG_PROPERTIES = "org/apache/ibatis/databases/blog/blog-derby.properties";
        driver=org.apache.derby.jdbc.EmbeddedDriver
        url=jdbc:derby:ibderby;create=true
        username=
        password=
    
        将配置文件中的配置读取到props中,使用mybatis的org.apache.ibatis.io.Resources完成,并创建一个org.apache.ibatis.datasource.unpooled.UnpooledDataSource返回
    */
    Properties props = Resources.getResourceAsProperties(resource);
    UnpooledDataSource ds = new UnpooledDataSource();
    ds.setDriver(props.getProperty("driver"));
    ds.setUrl(props.getProperty("url"));
    ds.setUsername(props.getProperty("username"));
    ds.setPassword(props.getProperty("password"));
    return ds;
    }
    
    • runScript(ds, BLOG_DDL);
        /*
            resource:public static final String BLOG_DDL = "org/apache/ibatis/databases/blog/blog-derby-schema.sql"; 一个sql脚本
        */
        public static void runScript(DataSource ds, String resource) throws IOException, SQLException {
        /*
            通过上面存储的org.apache.derby.jdbc.EmbeddedDriver生成一个Driver实例,之后存储在字段Map<String, Driver> registeredDrivers = new ConcurrentHashMap<String, Driver>();中;
            在通过Connection connection = DriverManager.getConnection(url, properties);生成一个url=jdbc:derby:ibderby;create=true的数据库链接,props中仅存储usename和password
            实际就是:
            private Connection doGetConnection(Properties properties) throws SQLException {
    initializeDriver();在这里往DriverManager中注册了以driver=org.apache.derby.jdbc.EmbeddedDriver对应驱动的实例
    Connection connection = DriverManager.getConnection(url, properties);这里驱动管理就会根据url去打开一个标准的数据库连接
    configureConnection(connection);
    return connection;
    }
        */
        Connection connection = ds.getConnection();//得到一个标准的java.sql.Connection
        try {
          ScriptRunner runner = new ScriptRunner(connection);//org.apache.ibatis.jdbc.ScriptRunner
          runner.setAutoCommit(true);
          runner.setStopOnError(false);
          runner.setLogWriter(null);
          runner.setErrorLogWriter(null);
          /*
            关键:通过jdbc的方式将sql脚本中的命令跑完
    
             private void executeLineByLine(Reader reader) {
                StringBuilder command = new StringBuilder();
                try {
                  BufferedReader lineReader = new BufferedReader(reader);
                  String line;
                  while ((line = lineReader.readLine()) != null) {
                    command = handleLine(command, line);
                  }
                  commitConnection();
                  checkForMissingLineTerminator(command);
                } catch (Exception e) {
                  String message = "Error executing: " + command + ".  Cause: " + e;
                  printlnError(message);
                  throw new RuntimeSqlException(message, e);
                }
              }
    
             private void executeStatement(String command) throws SQLException {
                boolean hasResults = false;
                Statement statement = connection.createStatement();
                statement.setEscapeProcessing(escapeProcessing);
                String sql = command;
                if (removeCRs) {
                  sql = sql.replaceAll("\r\n", "\n");
                }
                if (stopOnError) {
                  hasResults = statement.execute(sql);
                } else {
                  try {
                    hasResults = statement.execute(sql);
                  } catch (SQLException e) {
                    String message = "Error executing: " + command + ".  Cause: " + e;
                    printlnError(message);
                  }
                }
                printResults(statement, hasResults);
                try {
                  statement.close();
                } catch (Exception e) {
                  // Ignore to workaround a bug in some connection pools
                }
              }
          */
          runScript(runner, resource);
        } finally {
          connection.close();
        }
      }
    
    • runScript(ds, BLOG_DATA);
  • final Reader reader = Resources.getResourceAsReader(resource);

        public static Reader getResourceAsReader(String resource) throws IOException {
            Reader reader;
            if (charset == null) {
              //通过org.apache.ibatis.io.ClassLoaderWrapper来查找可用的类加载器去加载资源
              reader = new InputStreamReader(getResourceAsStream(resource));
            } else {
              ...
            }
            return reader;
        }
    
    • org.apache.ibatis.io.ClassLoaderWrapper 这个类比较实用
        public class ClassLoaderWrapper {
    
          ClassLoader defaultClassLoader;
          ClassLoader systemClassLoader;
    
          ClassLoaderWrapper() {
            try {
              systemClassLoader = ClassLoader.getSystemClassLoader();
            } catch (SecurityException ignored) {
              // AccessControlException on Google App Engine   
            }
    
            ClassLoader[] getClassLoaders(ClassLoader classLoader) {
    return new ClassLoader[]{
                classLoader,
                defaultClassLoader,
                Thread.currentThread().getContextClassLoader(),
                getClass().getClassLoader(),
                systemClassLoader};
          }
    
          public InputStream getResourceAsStream(String resource, ClassLoader classLoader) {
            return getResourceAsStream(resource, getClassLoaders(classLoader));
          }
    
          /*
           * Try to get a resource from a group of classloaders
           *
           * @param resource    - the resource to get
           * @param classLoader - the classloaders to examine
           * @return the resource or null
           */
          InputStream getResourceAsStream(String resource, ClassLoader[] classLoader) {
            for (ClassLoader cl : classLoader) {
              if (null != cl) {
    
                // try to find the resource as passed
                InputStream returnValue = cl.getResourceAsStream(resource);
    
                // now, some class loaders want this leading "/", so we'll add it and try again if we didn't find the resource
                if (null == returnValue) {
                  returnValue = cl.getResourceAsStream("/" + resource);
                }
    
                if (null != returnValue) {
                  return returnValue;
                }
              }
            }
            return null;
      }
    
      }
    
  • sqlMapper = new SqlSessionFactoryBuilder().build(reader);

        public class SqlSessionFactoryBuilder {
    
          //1
          public SqlSessionFactory build(Reader reader) {
            return build(reader, null, null);
          }
    
          //2
          public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
            try {
              XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
              return build(parser.parse());
            } catch (Exception e) {
              throw ExceptionFactory.wrapException("Error building SqlSession.", e);
            } finally {
              ErrorContext.instance().reset();
              try {
                reader.close();
              } catch (IOException e) {
                // Intentionally ignore. Prefer previous error.
              }
            }
      }
    
    
    • XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);

      配置文件构件对象

        public class XMLConfigBuilder extends BaseBuilder {
    
          private boolean parsed;
          private XPathParser parser;
          private String environment;
          private ReflectorFactory localReflectorFactory = new DefaultReflectorFactory();
    
          /*
    
          继承的属性
    
          protected final Configuration configuration; 该对象存储系统的配置信息(对象),比如配置的plugin拦截器将会以实例的方式存储configuration.addInterceptor(interceptorInstance);
    
          protected final TypeAliasRegistry typeAliasRegistry; 该对象和this.configuration.getTypeAliasRegistry()是同一个,一个别名映射的map缓存对象,在配置文件中可以使用默认的别名作为任何可以指定类的地方比如<plugin interceptor="org.apache.ibatis.builder.ExamplePlugin">或者在typeAliases解析之后读取自定义别名,或者在mapper中使用,其中利用Class<?> resolveClass(String alias)可以将别名转换成clazz对象;
          比如在解析插件配置的时候:Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
          如果没有找到对应的别名,那么该方法将会使用class.forname来生成clazz对象;
    
          */
    
        //1
          public XMLConfigBuilder(Reader reader) {
            this(reader, null, null);
          }
        //2 XPathParser创建的时候将会将配置文件生成一个dom对象:document
          public XMLConfigBuilder(Reader reader, String environment, Properties props) {
            this(new XPathParser(reader, true, props, new XMLMapperEntityResolver()), environment, props);
      }
    
        private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
        super(new Configuration());//config对象初始化的时候会设置一些mybatis默认将会使用的对象的类型注册如typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);,其中TypeAliasRegistry 负责管理别名, 这儿就是通过TypeAliasRegistry 进行别名注册, 通过一个HashMap来实现, key为别名, value就是别名对应的类型(class对象),系统在初始化该对象的时候会设置默认的registerAlias("integer[]", Integer[].class);而我们配置的<typeAliases>如果是以<package name="domain.blog"/>的方式配置,也将会在typeAliasesElement(root.evalNode("typeAliases"));的时候解析到其中,这个别名的主要用途还是在mapper配置文件的parameterType或者resultType上面,不用写权限定的包名和类名,直接写别名就ok
        ErrorContext.instance().resource("SQL Mapper Configuration");//一个内部上下文对象
        this.configuration.setVariables(props);
        this.parsed = false;
        this.environment = environment;//null
        this.parser = parser;//XPathParser
      }
    
      public Configuration parse() {
        if (parsed) {//只能调用一次
          throw new BuilderException("Each XMLConfigBuilder can only be used once.");
        }
        parsed = true;
        parseConfiguration(parser.evalNode("/configuration"));//利用XPathParser对象读取配置文件的跟对象,使用XPath表达式,细节:Node node = (Node) evaluate(expression, root, XPathConstants.NODE);,即用封装的标准的xpath对象完成:return xpath.evaluate(expression, root, returnType);,然后封装成自己的XNode对象:return new XNode(this, node, variables);,XNode将会的去一些Node对象的信息作为成员以便之后查找
        return configuration;
      }
    
        //在读取到根节点对象之后,通过该方法读取子节点的详细配置信息
        private void parseConfiguration(XNode root) {
            try {
              //issue #117 read properties first
              propertiesElement(root.evalNode("properties"));//根据其官网介绍,该元素低下用于配置一些外部的properties或者以子元素设置的预制项,提供给配置文件中其他地方以进行动态的设置:<property name="driver" value="${driver}"/>比如这里的驱动类配置,需要明确的就是内联配置(子元素),将会比外部配置具有更高的优先级
              typeAliasesElement(root.evalNode("typeAliases"));//进行别名的注册,将指定配置信息以mybatis别名的方式存储到:configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);或者 typeAliasRegistry.registerAlias(alias, clazz);中,详细可以参考:http://www.cnblogs.com/dongying/p/4037678.html
              pluginElement(root.evalNode("plugins"));//解析插件并缓存到configuration.interceptorChain成员变量中
              objectFactoryElement(root.evalNode("objectFactory"));//MyBatis uses an ObjectFactory to create all needed new Objects.objectFactory是一个mybatis的对象生成器,底层使用java反射完成
              objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
              reflectionFactoryElement(root.evalNode("reflectionFactory"));
              settingsElement(root.evalNode("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);
            }
    }
    
        //将 <properties resource="org/apache/ibatis/databases/blog/blog-derby.properties"/>配置的资源文件读取成prop对象并设置到XPathParser parser和Configuration configuration的Variables中缓存
        private void propertiesElement(XNode context) throws Exception {
        if (context != null) {
          Properties defaults = context.getChildrenAsProperties();
          //!
          String resource = context.getStringAttribute("resource");
          String url = context.getStringAttribute("url");
          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.");
          }
          if (resource != null) {
            defaults.putAll(Resources.getResourceAsProperties(resource));
          } else if (url != null) {
            defaults.putAll(Resources.getUrlAsProperties(url));
          }
          Properties vars = configuration.getVariables();
          if (vars != null) {
            defaults.putAll(vars);
          }
          parser.setVariables(defaults);
          configuration.setVariables(defaults);
        }
      }
    
      private void mapperElement(XNode parent) throws Exception {
            if (parent != null) {
              for (XNode child : parent.getChildren()) {
                if ("package".equals(child.getName())) {
                /*
                如果配置是这样:
                <!-- Register all interfaces in a package as mappers -->
                <mappers>
                  <package name="org.mybatis.builder"/>
                </mappers>
                */
                  String mapperPackage = child.getStringAttribute("name");
                  configuration.addMappers(mapperPackage);
                } else {
                /*
                或者:
                <!-- Using classpath relative resources -->
                <mappers>
                  <mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
                </mappers>
                <!-- Using url fully qualified paths -->
                <mappers>
                  <mapper url="file:///var/mappers/AuthorMapper.xml"/>
                </mappers>
                <!-- Using mapper interface classes -->
                <mappers>
                  <mapper class="org.mybatis.builder.AuthorMapper"/>
                </mappers>
                下面3行即列出了支持上面的3种配置方式
                */
    
                  String resource = child.getStringAttribute("resource");
                  String url = child.getStringAttribute("url");
                  String mapperClass = child.getStringAttribute("class");
                  if (resource != null && url == null && mapperClass == null) {//解析<mapper resource="org/apache/ibatis/builder/AuthorMapper.xml"/>类型的配置
                    ErrorContext.instance().resource(resource);
                    //读取配置文件
                    InputStream inputStream = Resources.getResourceAsStream(resource);
                    /*
                    又进入一个解析循环,解析主配置文件使用的是XMLConfigBuilder:
                        public XMLMapperBuilder(InputStream inputStream, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {
                            this(new XPathParser(inputStream, true, configuration.getVariables(), new XMLMapperEntityResolver()),
                                configuration, resource, sqlFragments);
                          }
                          但是这里会将全局的configuration对象传递进来了,但是是针对解析mapper的构造器,里面会有一个重要的this.builderAssistant = new MapperBuilderAssistant(configuration, 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.");
                  }
                }
              }
            }
          }
    
    

    mapperParser.parse();

         public void parse() {
            if (!configuration.isResourceLoaded(resource)) {//查看configuration.loadedResources.contains(resource);--》Set<String> loadedResources是否已经缓存过,即加载过该配置文件
            //先获取根节点再解析原始
              configurationElement(parser.evalNode("/mapper"));//解析根节点
              configuration.addLoadedResource(resource);
              bindMapperForNamespace();
            }
    
            parsePendingResultMaps();
            parsePendingChacheRefs();
            parsePendingStatements();
          }
    
    

    configurationElement(parser.evalNode(/mapper));
    解析跟节点

        private void configurationElement(XNode context) {
            try {
              String namespace = context.getStringAttribute("namespace");//从mapper元素的xnode对象中获取namespace属性的值;
              if (namespace == null || namespace.equals("")) {
              //如果没有设置框架是不允许的
                throw new BuilderException("Mapper's namespace cannot be empty");
              }
    
              builderAssistant.setCurrentNamespace(namespace);//将命名空间缓存到MapperBuilderAssistant的成员变量中;
              cacheRefElement(context.evalNode("cache-ref"));
              cacheElement(context.evalNode("cache"));
    
              /*
              读取:
              <parameterMap id="selectAuthor" type="org.apache.ibatis.domain.blog.Author">
        <parameter property="id" />
    </parameterMap>
    
              */
              parameterMapElement(context.evalNodes("/mapper/parameterMap"));
              /*
              读取resultMap元素,对应的模型是ResultMap,最后放到config的Map<String, ResultMap> resultMaps中进行存储
              */
              resultMapElements(context.evalNodes("/mapper/resultMap"));
              sqlElement(context.evalNodes("/mapper/sql"));
              /*
              最主要的一块,解析sql语句
              */
              buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
            } catch (Exception e) {
              throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);
            }
          }
    

    解析sql语句

    private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
    
    for (XNode context : list) {//list为"select|insert|update|delete"的节点集合
      final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
      try {
        statementParser.parseStatementNode();
      } catch (IncompleteElementException e) {
        configuration.addIncompleteStatement(statementParser);
      }
    }
    }
    
    //涉及到的关键代码
    public void parseStatementNode() {
    String id = context.getStringAttribute("id");//解析id
    String databaseId = context.getStringAttribute("databaseId");
    
    //下面这部检查会将id重新赋值为命名空间加id的形式
    if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
      return;
    }
    
    Integer fetchSize = context.getIntAttribute("fetchSize");
    Integer timeout = context.getIntAttribute("timeout");
    String parameterMap = context.getStringAttribute("parameterMap");
    String parameterType = context.getStringAttribute("parameterType");
    Class<?> parameterTypeClass = resolveClass(parameterType);
    String resultMap = context.getStringAttribute("resultMap");
    String resultType = context.getStringAttribute("resultType");
    String lang = context.getStringAttribute("lang");
    LanguageDriver langDriver = getLanguageDriver(lang);
    
    Class<?> resultTypeClass = resolveClass(resultType);
    String resultSetType = context.getStringAttribute("resultSetType");
    StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
    ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
    
    String nodeName = context.getNode().getNodeName();//解析节点到底是select还是...其他
    SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));//检查是什么类型
    boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
    boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
    boolean useCache = context.getBooleanAttribute("useCache", isSelect);
    boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);
    
    // Include Fragments before parsing
    XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
    includeParser.applyIncludes(context.getNode());//将sql中使用到全局properties-->${}的地方替换成响应的值
    
    // Parse selectKey after includes and remove them.
    processSelectKeyNodes(id, parameterTypeClass, langDriver);
    
    // Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
    SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);//将sql和参数集合封装成模型对象StaticSqlSource
    String resultSets = context.getStringAttribute("resultSets");
    String keyProperty = context.getStringAttribute("keyProperty");
    String keyColumn = context.getStringAttribute("keyColumn");
    KeyGenerator keyGenerator;
    String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
    keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
    if (configuration.hasKeyGenerator(keyStatementId)) {
      keyGenerator = configuration.getKeyGenerator(keyStatementId);
    } else {
      keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
          configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
          ? new Jdbc3KeyGenerator() : new NoKeyGenerator();
    }
    
    builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
        fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
        resultSetTypeEnum, flushCache, useCache, resultOrdered, 
        keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);//将sql字符串语句解析为内部模型对象MappedStatement的实现类,并且和上面解析到的resultMap和parameterMap等属性对应封装成一个,并存储到config对象(Map<String, MappedStatement> mappedStatements)中
    }
    

    解析parameterMap元素:将所有的parameterMap节点集传递进来解析

    private void parameterMapElement(List<XNode> list) throws Exception {
    for (XNode parameterMapNode : list) {
    //每一个节点都是一个xnode
    /*
    参考:
    <parameterMap id="selectAuthor" type="org.apache.ibatis.domain.blog.Author">
        <parameter property="id" />
    </parameterMap>
    */
      String id = parameterMapNode.getStringAttribute("id");//读取id
      String type = parameterMapNode.getStringAttribute("type");
      Class<?> parameterClass = resolveClass(type);//查看是否使用的是别名:
      /*
      从基类的:
       protected Class<?> resolveAlias(String alias) {
        return typeAliasRegistry.resolveAlias(alias);
      }
    
      public <T> Class<T> resolveAlias(String string) {
        try {
          if (string == null) {
            return null;
          }
          // issue #748 看github上面的issue讨论
          String key = string.toLowerCase(Locale.ENGLISH); 全部转换为小写
          Class<T> value;
    
    TYPE_ALIASES该对象初始化的时候已经注册了很多别名映射      
          public TypeAliasRegistry() {
    registerAlias("string", String.class);
    
    registerAlias("byte", Byte.class);
    registerAlias("long", Long.class);
    registerAlias("short", Short.class);
    registerAlias("int", Integer.class);
    
          if (TYPE_ALIASES.containsKey(key)) {
            value = (Class<T>) TYPE_ALIASES.get(key);
          } else {
            value = (Class<T>) Resources.classForName(string);如果没有找到那么就return classLoaderWrapper.classForName(className);这个有用的反射包裹器来返回clazz
          }
          return value;
        } catch (ClassNotFoundException e) {
          throw new TypeException("Could not resolve type alias '" + string + "'.  Cause: " + e, e);
        }
      }
      */
      List<XNode> parameterNodes = parameterMapNode.evalNodes("parameter");
    
      //解析<parameter property="id" />...子元素配置
      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);//得到java针对parameterMap的领域模型,底层会去将type对应的对象模型和<parameter property="id" />属性对应上,即比如这个模型的id是int类型,那么这个类型值将存储到parameterMapping中
        parameterMappings.add(parameterMapping);
      }
      builderAssistant.addParameterMap(id, parameterClass, parameterMappings);//以id为标识放在对应的属性中,而最终的id标识,是使用命名空间+'id'的形式:org.apache.ibatis.domain.blog.mappers.AuthorMapper.selectAuthor
      /*
      public ParameterMap addParameterMap(String id, Class<?> parameterClass, List<ParameterMapping> parameterMappings) {
            id = applyCurrentNamespace(id, false);拼id
            ParameterMap.Builder parameterMapBuilder = new ParameterMap.Builder(configuration, id, parameterClass, parameterMappings);
            ParameterMap parameterMap = parameterMapBuilder.build();
            也将添加到configuration:parameterMaps.put(pm.getId(), pm);
            configuration.addParameterMap(parameterMap);
            return parameterMap;
          }
      */
    }
    }
    
    • XMLMapperEntityResolver
        /**
         * Offline entity resolver for the MyBatis DTDs
         * 
         * @author Clinton Begin
         */
        public class XMLMapperEntityResolver implements EntityResolver {
    
          private static final Map<String, String> doctypeMap = new HashMap<String, String>();
    
          private static final String IBATIS_CONFIG_PUBLIC = "-//ibatis.apache.org//DTD Config 3.0//EN".toUpperCase(Locale.ENGLISH);
          private static final String IBATIS_CONFIG_SYSTEM = "http://ibatis.apache.org/dtd/ibatis-3-config.dtd".toUpperCase(Locale.ENGLISH);
    
          private static final String IBATIS_MAPPER_PUBLIC = "-//ibatis.apache.org//DTD Mapper 3.0//EN".toUpperCase(Locale.ENGLISH);
          private static final String IBATIS_MAPPER_SYSTEM = "http://ibatis.apache.org/dtd/ibatis-3-mapper.dtd".toUpperCase(Locale.ENGLISH);
    
          private static final String MYBATIS_CONFIG_PUBLIC = "-//mybatis.org//DTD Config 3.0//EN".toUpperCase(Locale.ENGLISH);
          private static final String MYBATIS_CONFIG_SYSTEM = "http://mybatis.org/dtd/mybatis-3-config.dtd".toUpperCase(Locale.ENGLISH);
    
          private static final String MYBATIS_MAPPER_PUBLIC = "-//mybatis.org//DTD Mapper 3.0//EN".toUpperCase(Locale.ENGLISH);
          private static final String MYBATIS_MAPPER_SYSTEM = "http://mybatis.org/dtd/mybatis-3-mapper.dtd".toUpperCase(Locale.ENGLISH);
    
          private static final String MYBATIS_CONFIG_DTD = "org/apache/ibatis/builder/xml/mybatis-3-config.dtd";
          private static final String MYBATIS_MAPPER_DTD = "org/apache/ibatis/builder/xml/mybatis-3-mapper.dtd";
    
          static {
               doctypeMap.put(IBATIS_CONFIG_SYSTEM, MYBATIS_CONFIG_DTD);
               doctypeMap.put(IBATIS_CONFIG_PUBLIC, MYBATIS_CONFIG_DTD);
    
               doctypeMap.put(IBATIS_MAPPER_SYSTEM, MYBATIS_MAPPER_DTD);
               doctypeMap.put(IBATIS_MAPPER_PUBLIC, MYBATIS_MAPPER_DTD);
    
               doctypeMap.put(MYBATIS_CONFIG_SYSTEM, MYBATIS_CONFIG_DTD);
               doctypeMap.put(MYBATIS_CONFIG_PUBLIC, MYBATIS_CONFIG_DTD);
    
               doctypeMap.put(MYBATIS_MAPPER_SYSTEM, MYBATIS_MAPPER_DTD);
               doctypeMap.put(MYBATIS_MAPPER_PUBLIC, MYBATIS_MAPPER_DTD);
         }
    
    • XPathParser
      • 持有一个jdk xpath解析器
      • 持有一个properties成员变量以存储配置文件中<properties resource="org/apache/ibatis/databases/blog/blog-derby.properties"/>设置的内容,以供其他配置解析时使用,即可以使用${}的方式设置Node.CDATA_SECTION_NODE、Node.TEXT_NODE两种类型的节点值;
            private String getBodyData(Node child) {
                if (child.getNodeType() == Node.CDATA_SECTION_NODE
                    || child.getNodeType() == Node.TEXT_NODE) {
                  String data = ((CharacterData) child).getData();
                  data = PropertyParser.parse(data, variables);
                  return data;
                }
                return null;
              }
    
              public static String parse(String string, Properties variables) {
                   VariableTokenHandler handler = new VariableTokenHandler(variables);
                   GenericTokenParser parser = new GenericTokenParser("${", "}", handler);
                   return parser.parse(string);
                 }
        ```
    
    ```java
    
        public class XPathParser {
    
          private Document document;
          private boolean validation;
          private EntityResolver entityResolver;
          private Properties variables;
          private XPath xpath;
    
           public XPathParser(Reader reader, boolean validation, Properties variables, EntityResolver entityResolver) {
           commonConstructor(validation, variables, entityResolver);//设置成员变量,其中validation:true,entityResolver即XMLMapperEntityResolver,variables为空还构建了一个xpath的实例
           this.document = createDocument(new InputSource(reader));//将配置文件生成一个dom对象:document
         }
    
    
    • this.document = createDocument(new InputSource(reader));

      会先创建一个org.xml.sax.InputSource(A single input source for an XML entity.),之后去读取它

          private Document createDocument(InputSource inputSource) {
      // important: this must only be called AFTER common constructor
      try {
      //使用标准的dom解析器
      DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
      factory.setValidating(validation);//true 对xml进行校验
      
      factory.setNamespaceAware(false);
      factory.setIgnoringComments(true);
      factory.setIgnoringElementContentWhitespace(false);
      factory.setCoalescing(false);
      factory.setExpandEntityReferences(true);
      
      DocumentBuilder builder = factory.newDocumentBuilder();
      builder.setEntityResolver(entityResolver);
      builder.setErrorHandler(new ErrorHandler() {
      @Override
      public void error(SAXParseException exception) throws SAXException {
        throw exception;
      }
      
      @Override
      public void fatalError(SAXParseException exception) throws SAXException {
        throw exception;
      }
      
      @Override
      public void warning(SAXParseException exception) throws SAXException {
      }
      });
      return builder.parse(inputSource);
      } catch (Exception e) {
      throw new BuilderException("Error creating document instance.  Cause: " + e, e);
      }
      }
      
      

未经允许不得转载:SEARU.ORG » MyBatis解析自身配置文件记录

赞 (0)
分享到:更多 ()

评论 0