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接口的映射工作了。



posted @ 2019-07-16 18:45  賣贾笔的小男孩  阅读(572)  评论(0编辑  收藏  举报