一、 介绍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 }
最后由SqlSessionFactoryBuilder返回的DefaultSqlSessionFactory的openSession()方法获取session,这里Mybatis的初始化就完成了,剩下的是mapper接口的映射工作了。