• MyBatis源码分析(三):MyBatis初始化(配置文件读取和解析)


    一、 介绍MyBatis初始化过程

      项目是简单的Mybatis应用,编写SQL Mapper,还有编写的SqlSessionFactoryUtil里面用了Mybatis的IO包里面的Resources获取配置文件的输入流,利用SqlSessionFactoryBuilder获取创建Session的工厂。

      首先构建的是承载mybatis-config配置的Configuration类,它是由SqlSessionFactoryBuilder的build开始的,时序图如下:

     二、相关代码

    自己编写的SqlSessionFactoryUtil.java

     1 public class SqlSessionFactoryUtil {
     2     //SQLSessionFactory对象
     3     private static SqlSessionFactory sqlSessionFactory = null;
     4     //类线程锁
     5     private static final Class CLASS_LOCK = SqlSessionFactoryUtil.class;
     6 
     7     private SqlSessionFactoryUtil() {}
     8 
     9     /**
    10      * 构建SqlSessionFactory
    11      */
    12     public static SqlSessionFactory init() {
    13         String resource = "mybatis-config.xml";
    14         InputStream inputStream = null;
    15         try {
    16             inputStream = Resources.getResourceAsStream(resource);
    17         } catch (IOException ex) {
    18             Logger.getLogger(SqlSessionFactoryUtil.class.getName()).log(Level.SEVERE, null, ex);
    19         }
    20         synchronized(CLASS_LOCK) {
    21             if(sqlSessionFactory == null) {
    22                 sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    23             }
    24         }
    25         return sqlSessionFactory;
    26     }
    27 
    28     /**
    29      * 打开SqlSession
    30      */
    31     public static SqlSession openSqlSession() {
    32         if (sqlSessionFactory == null) {
    33             init();
    34         }
    35         return sqlSessionFactory.openSession();
    36     }
    37 }

    源码 SqlSessionFactoryBuilder.java,首先是读取配置到Configuration类,再利用读取出来的config构建DefaultSqlSessionFactory

     1 public class SqlSessionFactoryBuilder {
     2 
     3   public SqlSessionFactory build(Reader reader) {
     4     return build(reader, null, null);
     5   }
     6 
     7   public SqlSessionFactory build(Reader reader, String environment) {
     8     return build(reader, environment, null);
     9   }
    10 
    11   public SqlSessionFactory build(Reader reader, Properties properties) {
    12     return build(reader, null, properties);
    13   }
    14 
    15   public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
    16     try {
    17       XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
    18       return build(parser.parse());
    19     } catch (Exception e) {
    20       throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    21     } finally {
    22       ErrorContext.instance().reset();
    23       try {
    24         reader.close();
    25       } catch (IOException e) {
    26         // Intentionally ignore. Prefer previous error.
    27       }
    28     }
    29   }
    30   //使用的是这个方法构建SqlSessionFactory
    31   public SqlSessionFactory build(InputStream inputStream) {
    32     return build(inputStream, null, null);
    33   }
    34 
    35   public SqlSessionFactory build(InputStream inputStream, String environment) {
    36     return build(inputStream, environment, null);
    37   }
    38 
    39   public SqlSessionFactory build(InputStream inputStream, Properties properties) {
    40     return build(inputStream, null, properties);
    41   }
    42   //构建build
    43   public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    44     try {
    45       XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties); //XMLConfigBuilder解析mybatis-config.xml配置
    46       return build(parser.parse());
    47     } catch (Exception e) {
    48       throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    49     } finally {
    50       ErrorContext.instance().reset();
    51       try {
    52         inputStream.close();
    53       } catch (IOException e) {
    54         // Intentionally ignore. Prefer previous error.
    55       }
    56     }
    57   }
    58 
    59   public SqlSessionFactory build(Configuration config) {
    60     return new DefaultSqlSessionFactory(config);
    61   }
    62 
    63 }


    源码 XMLConfigBuilder.java,读取并保存mybatis-config配置文件中大部分节点属性
      1 public class XMLConfigBuilder extends BaseBuilder {
      2 
      3   private boolean parsed;
      4   private final XPathParser parser;
      5   private String environment;
      6   private final ReflectorFactory localReflectorFactory = new DefaultReflectorFactory();
      7 
      8   public XMLConfigBuilder(Reader reader) {
      9     this(reader, null, null);
     10   }
     11 
     12   public XMLConfigBuilder(Reader reader, String environment) {
     13     this(reader, environment, null);
     14   }
     15 
     16   public XMLConfigBuilder(Reader reader, String environment, Properties props) {
     17     this(new XPathParser(reader, true, props, new XMLMapperEntityResolver()), environment, props);
     18   }
     19 
     20   public XMLConfigBuilder(InputStream inputStream) {
     21     this(inputStream, null, null);
     22   }
     23 
     24   public XMLConfigBuilder(InputStream inputStream, String environment) {
     25     this(inputStream, environment, null);
     26   }
     27 
     28   public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
     29     this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
     30   }
     31 
     32   private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
     33     super(new Configuration());
     34     ErrorContext.instance().resource("SQL Mapper Configuration");
     35     this.configuration.setVariables(props);
     36     this.parsed = false;
     37     this.environment = environment;
     38     this.parser = parser;
     39   }
     40 
     41   public Configuration parse() {
     42     if (parsed) {
     43       throw new BuilderException("Each XMLConfigBuilder can only be used once.");
     44     }
     45     parsed = true;
     46     parseConfiguration(parser.evalNode("/configuration"));
     47     return configuration;
     48   }
     49 
     50   private void parseConfiguration(XNode root) {
     51     try {
     52       //issue #117 read properties first
     53       propertiesElement(root.evalNode("properties"));
     54       Properties settings = settingsAsProperties(root.evalNode("settings"));
     55       loadCustomVfs(settings);
     56       loadCustomLogImpl(settings);
     57       typeAliasesElement(root.evalNode("typeAliases"));
     58       pluginElement(root.evalNode("plugins"));
     59       objectFactoryElement(root.evalNode("objectFactory"));
     60       objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
     61       reflectorFactoryElement(root.evalNode("reflectorFactory"));
     62       settingsElement(settings);
     63       // read it after objectFactory and objectWrapperFactory issue #631
     64       environmentsElement(root.evalNode("environments"));
     65       databaseIdProviderElement(root.evalNode("databaseIdProvider"));
     66       typeHandlerElement(root.evalNode("typeHandlers"));
     67       mapperElement(root.evalNode("mappers"));
     68     } catch (Exception e) {
     69       throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
     70     }
     71   }
     72 
     73   private Properties settingsAsProperties(XNode context) {
     74     if (context == null) {
     75       return new Properties();
     76     }
     77     Properties props = context.getChildrenAsProperties();
     78     // Check that all settings are known to the configuration class
     79     MetaClass metaConfig = MetaClass.forClass(Configuration.class, localReflectorFactory);
     80     for (Object key : props.keySet()) {
     81       if (!metaConfig.hasSetter(String.valueOf(key))) {
     82         throw new BuilderException("The setting " + key + " is not known.  Make sure you spelled it correctly (case sensitive).");
     83       }
     84     }
     85     return props;
     86   }
     87 
     88   private void loadCustomVfs(Properties props) throws ClassNotFoundException {
     89     String value = props.getProperty("vfsImpl");
     90     if (value != null) {
     91       String[] clazzes = value.split(",");
     92       for (String clazz : clazzes) {
     93         if (!clazz.isEmpty()) {
     94           @SuppressWarnings("unchecked")
     95           Class<? extends VFS> vfsImpl = (Class<? extends VFS>)Resources.classForName(clazz);
     96           configuration.setVfsImpl(vfsImpl);
     97         }
     98       }
     99     }
    100   }
    101 
    102   private void loadCustomLogImpl(Properties props) {
    103     Class<? extends Log> logImpl = resolveClass(props.getProperty("logImpl"));
    104     configuration.setLogImpl(logImpl);
    105   }
    106 
    107   private void typeAliasesElement(XNode parent) {
    108     if (parent != null) {
    109       for (XNode child : parent.getChildren()) {
    110         if ("package".equals(child.getName())) {
    111           String typeAliasPackage = child.getStringAttribute("name");
    112           configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);
    113         } else {
    114           String alias = child.getStringAttribute("alias");
    115           String type = child.getStringAttribute("type");
    116           try {
    117             Class<?> clazz = Resources.classForName(type);
    118             if (alias == null) {
    119               typeAliasRegistry.registerAlias(clazz);
    120             } else {
    121               typeAliasRegistry.registerAlias(alias, clazz);
    122             }
    123           } catch (ClassNotFoundException e) {
    124             throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);
    125           }
    126         }
    127       }
    128     }
    129   }
    130 
    131   private void pluginElement(XNode parent) throws Exception {
    132     if (parent != null) {
    133       for (XNode child : parent.getChildren()) {
    134         String interceptor = child.getStringAttribute("interceptor");
    135         Properties properties = child.getChildrenAsProperties();
    136         Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
    137         interceptorInstance.setProperties(properties);
    138         configuration.addInterceptor(interceptorInstance);
    139       }
    140     }
    141   }
    142 
    143   private void objectFactoryElement(XNode context) throws Exception {
    144     if (context != null) {
    145       String type = context.getStringAttribute("type");
    146       Properties properties = context.getChildrenAsProperties();
    147       ObjectFactory factory = (ObjectFactory) resolveClass(type).newInstance();
    148       factory.setProperties(properties);
    149       configuration.setObjectFactory(factory);
    150     }
    151   }
    152 
    153   private void objectWrapperFactoryElement(XNode context) throws Exception {
    154     if (context != null) {
    155       String type = context.getStringAttribute("type");
    156       ObjectWrapperFactory factory = (ObjectWrapperFactory) resolveClass(type).newInstance();
    157       configuration.setObjectWrapperFactory(factory);
    158     }
    159   }
    160 
    161   private void reflectorFactoryElement(XNode context) throws Exception {
    162     if (context != null) {
    163        String type = context.getStringAttribute("type");
    164        ReflectorFactory factory = (ReflectorFactory) resolveClass(type).newInstance();
    165        configuration.setReflectorFactory(factory);
    166     }
    167   }
    168 
    169   private void propertiesElement(XNode context) throws Exception {
    170     if (context != null) {
    171       Properties defaults = context.getChildrenAsProperties();
    172       String resource = context.getStringAttribute("resource");
    173       String url = context.getStringAttribute("url");
    174       if (resource != null && url != null) {
    175         throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference.  Please specify one or the other.");
    176       }
    177       if (resource != null) {
    178         defaults.putAll(Resources.getResourceAsProperties(resource));
    179       } else if (url != null) {
    180         defaults.putAll(Resources.getUrlAsProperties(url));
    181       }
    182       Properties vars = configuration.getVariables();
    183       if (vars != null) {
    184         defaults.putAll(vars);
    185       }
    186       parser.setVariables(defaults);
    187       configuration.setVariables(defaults);
    188     }
    189   }
    190 
    191   private void settingsElement(Properties props) {
    192     configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL")));
    193     configuration.setAutoMappingUnknownColumnBehavior(AutoMappingUnknownColumnBehavior.valueOf(props.getProperty("autoMappingUnknownColumnBehavior", "NONE")));
    194     configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true));
    195     configuration.setProxyFactory((ProxyFactory) createInstance(props.getProperty("proxyFactory")));
    196     configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty("lazyLoadingEnabled"), false));
    197     configuration.setAggressiveLazyLoading(booleanValueOf(props.getProperty("aggressiveLazyLoading"), false));
    198     configuration.setMultipleResultSetsEnabled(booleanValueOf(props.getProperty("multipleResultSetsEnabled"), true));
    199     configuration.setUseColumnLabel(booleanValueOf(props.getProperty("useColumnLabel"), true));
    200     configuration.setUseGeneratedKeys(booleanValueOf(props.getProperty("useGeneratedKeys"), false));
    201     configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE")));
    202     configuration.setDefaultStatementTimeout(integerValueOf(props.getProperty("defaultStatementTimeout"), null));
    203     configuration.setDefaultFetchSize(integerValueOf(props.getProperty("defaultFetchSize"), null));
    204     configuration.setMapUnderscoreToCamelCase(booleanValueOf(props.getProperty("mapUnderscoreToCamelCase"), false));
    205     configuration.setSafeRowBoundsEnabled(booleanValueOf(props.getProperty("safeRowBoundsEnabled"), false));
    206     configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope", "SESSION")));
    207     configuration.setJdbcTypeForNull(JdbcType.valueOf(props.getProperty("jdbcTypeForNull", "OTHER")));
    208     configuration.setLazyLoadTriggerMethods(stringSetValueOf(props.getProperty("lazyLoadTriggerMethods"), "equals,clone,hashCode,toString"));
    209     configuration.setSafeResultHandlerEnabled(booleanValueOf(props.getProperty("safeResultHandlerEnabled"), true));
    210     configuration.setDefaultScriptingLanguage(resolveClass(props.getProperty("defaultScriptingLanguage")));
    211     configuration.setDefaultEnumTypeHandler(resolveClass(props.getProperty("defaultEnumTypeHandler")));
    212     configuration.setCallSettersOnNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), false));
    213     configuration.setUseActualParamName(booleanValueOf(props.getProperty("useActualParamName"), true));
    214     configuration.setReturnInstanceForEmptyRow(booleanValueOf(props.getProperty("returnInstanceForEmptyRow"), false));
    215     configuration.setLogPrefix(props.getProperty("logPrefix"));
    216     configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory")));
    217   }
    218 
    219   private void environmentsElement(XNode context) throws Exception {
    220     if (context != null) {
    221       if (environment == null) {
    222         environment = context.getStringAttribute("default");
    223       }
    224       for (XNode child : context.getChildren()) {
    225         String id = child.getStringAttribute("id");
    226         if (isSpecifiedEnvironment(id)) {
    227           TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
    228           DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
    229           DataSource dataSource = dsFactory.getDataSource();
    230           Environment.Builder environmentBuilder = new Environment.Builder(id)
    231               .transactionFactory(txFactory)
    232               .dataSource(dataSource);
    233           configuration.setEnvironment(environmentBuilder.build());
    234         }
    235       }
    236     }
    237   }
    238 
    239   private void databaseIdProviderElement(XNode context) throws Exception {
    240     DatabaseIdProvider databaseIdProvider = null;
    241     if (context != null) {
    242       String type = context.getStringAttribute("type");
    243       // awful patch to keep backward compatibility
    244       if ("VENDOR".equals(type)) {
    245           type = "DB_VENDOR";
    246       }
    247       Properties properties = context.getChildrenAsProperties();
    248       databaseIdProvider = (DatabaseIdProvider) resolveClass(type).newInstance();
    249       databaseIdProvider.setProperties(properties);
    250     }
    251     Environment environment = configuration.getEnvironment();
    252     if (environment != null && databaseIdProvider != null) {
    253       String databaseId = databaseIdProvider.getDatabaseId(environment.getDataSource());
    254       configuration.setDatabaseId(databaseId);
    255     }
    256   }
    257 
    258   private TransactionFactory transactionManagerElement(XNode context) throws Exception {
    259     if (context != null) {
    260       String type = context.getStringAttribute("type");
    261       Properties props = context.getChildrenAsProperties();
    262       TransactionFactory factory = (TransactionFactory) resolveClass(type).newInstance();
    263       factory.setProperties(props);
    264       return factory;
    265     }
    266     throw new BuilderException("Environment declaration requires a TransactionFactory.");
    267   }
    268 
    269   private DataSourceFactory dataSourceElement(XNode context) throws Exception {
    270     if (context != null) {
    271       String type = context.getStringAttribute("type");
    272       Properties props = context.getChildrenAsProperties();
    273       DataSourceFactory factory = (DataSourceFactory) resolveClass(type).newInstance();
    274       factory.setProperties(props);
    275       return factory;
    276     }
    277     throw new BuilderException("Environment declaration requires a DataSourceFactory.");
    278   }
    279 
    280   private void typeHandlerElement(XNode parent) {
    281     if (parent != null) {
    282       for (XNode child : parent.getChildren()) {
    283         if ("package".equals(child.getName())) {
    284           String typeHandlerPackage = child.getStringAttribute("name");
    285           typeHandlerRegistry.register(typeHandlerPackage);
    286         } else {
    287           String javaTypeName = child.getStringAttribute("javaType");
    288           String jdbcTypeName = child.getStringAttribute("jdbcType");
    289           String handlerTypeName = child.getStringAttribute("handler");
    290           Class<?> javaTypeClass = resolveClass(javaTypeName);
    291           JdbcType jdbcType = resolveJdbcType(jdbcTypeName);
    292           Class<?> typeHandlerClass = resolveClass(handlerTypeName);
    293           if (javaTypeClass != null) {
    294             if (jdbcType == null) {
    295               typeHandlerRegistry.register(javaTypeClass, typeHandlerClass);
    296             } else {
    297               typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass);
    298             }
    299           } else {
    300             typeHandlerRegistry.register(typeHandlerClass);
    301           }
    302         }
    303       }
    304     }
    305   }
    306 
    307   private void mapperElement(XNode parent) throws Exception {
    308     if (parent != null) {
    309       for (XNode child : parent.getChildren()) {
    310         if ("package".equals(child.getName())) {
    311           String mapperPackage = child.getStringAttribute("name");
    312           configuration.addMappers(mapperPackage);
    313         } else {
    314           String resource = child.getStringAttribute("resource");
    315           String url = child.getStringAttribute("url");
    316           String mapperClass = child.getStringAttribute("class");
    317           if (resource != null && url == null && mapperClass == null) {
    318             ErrorContext.instance().resource(resource);
    319             InputStream inputStream = Resources.getResourceAsStream(resource);
    320             XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
    321             mapperParser.parse();
    322           } else if (resource == null && url != null && mapperClass == null) {
    323             ErrorContext.instance().resource(url);
    324             InputStream inputStream = Resources.getUrlAsStream(url);
    325             XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
    326             mapperParser.parse();
    327           } else if (resource == null && url == null && mapperClass != null) {
    328             Class<?> mapperInterface = Resources.classForName(mapperClass);
    329             configuration.addMapper(mapperInterface);
    330           } else {
    331             throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
    332           }
    333         }
    334       }
    335     }
    336   }
    337 
    338   private boolean isSpecifiedEnvironment(String id) {
    339     if (environment == null) {
    340       throw new BuilderException("No environment specified.");
    341     } else if (id == null) {
    342       throw new BuilderException("Environment requires an id attribute.");
    343     } else if (environment.equals(id)) {
    344       return true;
    345     }
    346     return false;
    347   }
    348 
    349 }
    View Code

     

      最后由SqlSessionFactoryBuilder返回的DefaultSqlSessionFactory的openSession()方法获取session,这里Mybatis的初始化就完成了,剩下的是mapper接口的映射工作了。



  • 相关阅读:
    构造函数初始化与赋值
    C代码中如何调用C++ C++中如何调用C
    C用函数指针模拟重载 C++重载
    【2019.10.30】意料之外的小黄衫——获得小黄衫感言
    【2019.10.30】SDN上机第1次作业
    【2019.10.17】十天Web前端程序员体验(软件工程实践第五次作业)
    【2019.10.07】《重生之我是程序员》
    【2019.09.30】“福大同好”——原型设计展示~
    【2019.09.30】构建之法《四五八章读后感》
    【2019.09.25】《构建之法》前三章读后感
  • 原文地址:https://www.cnblogs.com/magic-sea/p/11196778.html
Copyright © 2020-2023  润新知