MyBatis源码分析(三):MyBatis初始化(配置文件读取和解析)
阅读原文时间:2023年07月08日阅读:4

一、 介绍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 vfsImpl = (Class)Resources.classForName(clazz);
96 configuration.setVfsImpl(vfsImpl);
97 }
98 }
99 }
100 }
101
102 private void loadCustomLogImpl(Properties props) {
103 Class 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接口的映射工作了。