Mybatis源码浅析

源码阅读从测试类开始

 1 public class MybatisTest {
 2     public static void main(String[] args) throws Exception {
 3         // 读取配置文件
 4         InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
 5         // 创建SqlSessionFactory
 6         SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
 7         SqlSessionFactory factory = builder.build(in);//读取全局配置文件入口
 8         // 使用工厂生产sqlsession对象
 9         SqlSession sqlSession = factory.openSession();
10         // 使用sqlsession创建UserDao接口代理对象
11         UserDao userDao = sqlSession.getMapper(UserDao.class);
12         // 使用代理对象执行方法
13         List<User> all = userDao.findAll();
14         for (User user : all) {
15             System.out.println(user);
16         }
17         // 释放资源
18         sqlSession.close();
19         in.close();
20     }
21 }

一、根据SqlSessionFactory factory = builder.build(in)作为读取全局配置文件的入口

 1   public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
 2     try {
 3       XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
 4       return build(parser.parse());//根据传递的全局配置文件输入流使用xml解析器进行解析,在此处继续跟进此方法
 5     } catch (Exception e) {
 6       throw ExceptionFactory.wrapException("Error building SqlSession.", e);
 7     } finally {
 8       ErrorContext.instance().reset();
 9       try {
10         inputStream.close();
11       } catch (IOException e) {
12         // Intentionally ignore. Prefer previous error.
13       }
14     }
15   }
1  public Configuration parse() {
2     if (parsed) {
3       throw new BuilderException("Each XMLConfigBuilder can only be used once.");
4     }
5     parsed = true;
6     parseConfiguration(parser.evalNode("/configuration"));//在此处传递configration标签下的所有标签,在parseConfigration方法中继续解析,继续跟进方法
7     return configuration;
8   }

在此方法内解析全局配置文件的所有标签并保存到configration对象中

 1 private void parseConfiguration(XNode root) {
 2     try {
 3       //issue #117 read properties first
 4       propertiesElement(root.evalNode("properties"));
 5       Properties settings = settingsAsProperties(root.evalNode("settings"));
 6       loadCustomVfs(settings);
 7       typeAliasesElement(root.evalNode("typeAliases"));
 8       pluginElement(root.evalNode("plugins"));
 9       objectFactoryElement(root.evalNode("objectFactory"));
10       objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
11       reflectorFactoryElement(root.evalNode("reflectorFactory"));
12       settingsElement(settings);
13       // read it after objectFactory and objectWrapperFactory issue #631
14       environmentsElement(root.evalNode("environments"));
15       databaseIdProviderElement(root.evalNode("databaseIdProvider"));
16       typeHandlerElement(root.evalNode("typeHandlers"));
17       mapperElement(root.evalNode("mappers"));
18     } catch (Exception e) {
19       throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
20     }
21   }

1、properties标签解析过程

 1 private void propertiesElement(XNode context) throws Exception {
 2     if (context != null) {
 3       Properties defaults = context.getChildrenAsProperties(); // 此处首先将properties标签内的属性加载在defaults对象内(也就是如果直接将配置写在properties中会首先加载)
 4       String resource = context.getStringAttribute("resource");
 5       String url = context.getStringAttribute("url");
 6       if (resource != null && url != null) {
 7         throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference.  Please specify one or the other.");
 8       }
 9       if (resource != null) {
10         defaults.putAll(Resources.getResourceAsProperties(resource)); // 如果properties标签有resource属性,会根据resource读取配置文件然后将内容存储在defaults对象中
11       } else if (url != null) {
12         defaults.putAll(Resources.getUrlAsProperties(url)); // 如果properties标签有url属性,会根据url读取url资源,然后将读取内容存储在defaults对象中
13       }
14       Properties vars = configuration.getVariables(); // 此处为获取全局配置对象的Properties属性对象
15       if (vars != null) {
16         defaults.putAll(vars); // 将全局配置中的内容添加到defaults中
17       }
18       parser.setVariables(defaults); // 将添加properties内容的全局配置属性设置到解析器对象的variable属性中
19       configuration.setVariables(defaults); // 将添加properties属性后的全局配置属性添加到configration对象的variable属性中
20     }
21   }

此处可以看出properties如果配置了多种方式配置,优先级顺序为 url配置方式>resource方式>直接在properties内设置值方式

2、settings解析过程

3、typeAliases标签解析过程(只看package方式源码,直接配置方式同package方式)

 1 private void typeAliasesElement(XNode parent) {
 2     if (parent != null) {
 3       for (XNode child : parent.getChildren()) {
 4         if ("package".equals(child.getName())) { // 如果typeAliases标签使用的是package属性方式
 5           String typeAliasPackage = child.getStringAttribute("name"); // 获取package的name属性(包名)
 6           configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage); // 调用registerAliases方法将包名传入,跟进方法源码
 7         } else { // 如果typeAliases别名直接在标签内配置方式
 8           String alias = child.getStringAttribute("alias");
 9           String type = child.getStringAttribute("type");
10           try {
11             Class<?> clazz = Resources.classForName(type);
12             if (alias == null) {
13               typeAliasRegistry.registerAlias(clazz);
14             } else {
15               typeAliasRegistry.registerAlias(alias, clazz);
16             }
17           } catch (ClassNotFoundException e) {
18             throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);
19           }
20         }
21       }
22     }
23   }

 

 1 public void registerAliases(String packageName, Class<?> superType){  // 该方法是根据传递的参数(包名)解析包下的所有类
 2     ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>();
 3     resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
 4     Set<Class<? extends Class<?>>> typeSet = resolverUtil.getClasses();
 5     for(Class<?> type : typeSet){
 6       // Ignore inner classes and interfaces (including package-info.java)
 7       // Skip also inner classes. See issue #6
 8       if (!type.isAnonymousClass() && !type.isInterface() && !type.isMemberClass()) {  // 排除匿名类、接口、成员类(只要实体类)
 9         registerAlias(type); // 将筛选出来的实体类调用registerAliases方法,跟进源码
10       }
11     }
12   }

 

1 public void registerAlias(Class<?> type) {
2     String alias = type.getSimpleName(); // 获取类名
3     Alias aliasAnnotation = type.getAnnotation(Alias.class); // 获取该类的Alias注解
4     if (aliasAnnotation != null) { // 如果实体类已经配置Alias注解
5       alias = aliasAnnotation.value(); // 获取该注解的value值并赋值给alias变量
6     } 
7     registerAlias(alias, type); // 继续跟进方法
8   }

 

 1  public void registerAlias(String alias, Class<?> value) {
 2     if (alias == null) {
 3       throw new TypeException("The parameter alias cannot be null");
 4     }
 5     // issue #748
 6     String key = alias.toLowerCase(Locale.ENGLISH); // 将传递的alias值变小写
 7     if (TYPE_ALIASES.containsKey(key) && TYPE_ALIASES.get(key) != null && !TYPE_ALIASES.get(key).equals(value)) { // 判断该别名与已经注册的别名有无冲突
 8       throw new TypeException("The alias '" + alias + "' is already mapped to the value '" + TYPE_ALIASES.get(key).getName() + "'.");
 9     }
10     TYPE_ALIASES.put(key, value); // 将别名和类对应设置在TYPE_ALIASES的map集合中
11   }

4、plugins标签解析

5、objectFactory标签解析

6、objectWrapperFactory标签解析

7、reflectorFactory标签解析

8、environments标签解析

 1 private void environmentsElement(XNode context) throws Exception {
 2     if (context != null) {
 3       if (environment == null) {
 4         environment = context.getStringAttribute("default"); // 获取environments的defaults的值,用来指定要启用哪个environment配置
 5       }
 6       for (XNode child : context.getChildren()) {
 7         String id = child.getStringAttribute("id"); // 获取每个environment的id属性值
 8         if (isSpecifiedEnvironment(id)) {  // 找到id值和environments的defaults值相同的environment配置
 9           TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager")); // 创建事务工厂,跟进源码
10           DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource")); // 读取数据库配置文件生成数据库连接池工厂,跟进源码
11           DataSource dataSource = dsFactory.getDataSource(); //返回数据库连接池
12           Environment.Builder environmentBuilder = new Environment.Builder(id)
13               .transactionFactory(txFactory)
14               .dataSource(dataSource);
15           configuration.setEnvironment(environmentBuilder.build()); // 将Environment对象传递给configration的environment属性
16         }
17       }
18     }
19   }

 

 1 private TransactionFactory transactionManagerElement(XNode context) throws Exception {
 2     if (context != null) {
 3       String type = context.getStringAttribute("type");
 4       Properties props = context.getChildrenAsProperties();
 5       TransactionFactory factory = (TransactionFactory) resolveClass(type).newInstance();
 6       factory.setProperties(props);
 7       return factory; // 返回事务工厂对象
 8     }
 9     throw new BuilderException("Environment declaration requires a TransactionFactory.");
10   }

 

 1 private DataSourceFactory dataSourceElement(XNode context) throws Exception {
 2     if (context != null) {
 3       String type = context.getStringAttribute("type");
 4       Properties props = context.getChildrenAsProperties();
 5       DataSourceFactory factory = (DataSourceFactory) resolveClass(type).newInstance();
 6       factory.setProperties(props);
 7       return factory; // 返回数据库连接池对象
 8     }
 9     throw new BuilderException("Environment declaration requires a DataSourceFactory.");
10   }

9、databaseIdProvider标签解析

10、typeHandlers标签解析

11、mappers标签解析

 1  private void mapperElement(XNode parent) throws Exception {
 2     if (parent != null) {
 3       for (XNode child : parent.getChildren()) {
 4         if ("package".equals(child.getName())) { // 如果mapper配置了package属性,根据包名解析
 5           String mapperPackage = child.getStringAttribute("name");
 6           configuration.addMappers(mapperPackage); // 将配置的mapper设置configration的mappers属性中,跟进源码
 7         } else {  // 如果配置了resource、url、class,根据这些参数进行解析mapper
 8           String resource = child.getStringAttribute("resource");
 9           String url = child.getStringAttribute("url");
10           String mapperClass = child.getStringAttribute("class");
11           if (resource != null && url == null && mapperClass == null) {
12             ErrorContext.instance().resource(resource);
13             InputStream inputStream = Resources.getResourceAsStream(resource);
14             XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
15             mapperParser.parse();
16           } else if (resource == null && url != null && mapperClass == null) {
17             ErrorContext.instance().resource(url);
18             InputStream inputStream = Resources.getUrlAsStream(url);
19             XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
20             mapperParser.parse();
21           } else if (resource == null && url == null && mapperClass != null) {
22             Class<?> mapperInterface = Resources.classForName(mapperClass);
23             configuration.addMapper(mapperInterface);
24           } else {
25             throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
26           }
27         }
28       }
29     }
30   }

 

1 public void addMappers(String packageName, Class<?> superType) {
2     ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>();
3     resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
4     Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses();
5     for (Class<?> mapperClass : mapperSet) {
6       addMapper(mapperClass); //遍历package下的所有类传递给addMapper方法,继续跟进
7     }
8   }
 1 public <T> void addMapper(Class<T> type) {
 2     if (type.isInterface()) { //如果是接口类型
 3       if (hasMapper(type)) {
 4         throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
 5       }
 6       boolean loadCompleted = false;
 7       try {
 8         knownMappers.put(type, new MapperProxyFactory<T>(type));
 9         // It's important that the type is added before the parser is run
10         // otherwise the binding may automatically be attempted by the
11         // mapper parser. If the type is already known, it won't try.
12         MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
13         parser.parse(); // 解析每个接口 ,继续跟进
14         loadCompleted = true;
15       } finally {
16         if (!loadCompleted) {
17           knownMappers.remove(type);
18         }
19       }
20     }
21   }

 

 1 public void parse() {
 2     String resource = type.toString();
 3     if (!configuration.isResourceLoaded(resource)) { // 判断是否加载映射xml文件
 4       loadXmlResource(); 
 5       configuration.addLoadedResource(resource);
 6       assistant.setCurrentNamespace(type.getName());
 7       parseCache();
 8       parseCacheRef();
 9       Method[] methods = type.getMethods();
10       for (Method method : methods) {
11         try {
12           // issue #237
13           if (!method.isBridge()) {
14             parseStatement(method); // 解析dao接口的方法 如果有注解则解析没有就不解析
15           }
16         } catch (IncompleteElementException e) {
17           configuration.addIncompleteMethod(new MethodResolver(this, method));
18         }
19       }
20     }
21     parsePendingMethods();
22   }

 二、解析mapper映射配置文件

在XMLMapperBuilder.java类中:

 1  public void parse() {
 2     if (!configuration.isResourceLoaded(resource)) {
 3       configurationElement(parser.evalNode("/mapper")); //该方法就是解析mapper映射配置文件入口,跟进源码
 4       configuration.addLoadedResource(resource);
 5       bindMapperForNamespace();
 6     }
 7 
 8     parsePendingResultMaps();
 9     parsePendingCacheRefs();
10     parsePendingStatements();
11   }
 1 private void configurationElement(XNode context) {
 2     try {
 3       String namespace = context.getStringAttribute("namespace");
 4       if (namespace == null || namespace.equals("")) {
 5         throw new BuilderException("Mapper's namespace cannot be empty");
 6       }
 7       builderAssistant.setCurrentNamespace(namespace);
 8       cacheRefElement(context.evalNode("cache-ref"));
 9       cacheElement(context.evalNode("cache"));
10       parameterMapElement(context.evalNodes("/mapper/parameterMap"));
11       resultMapElements(context.evalNodes("/mapper/resultMap"));
12       sqlElement(context.evalNodes("/mapper/sql"));
13       buildStatementFromContext(context.evalNodes("select|insert|update|delete")); // 在此处跟进源码查看select|insert|update|delete如何封装为MappedStatement对象
14     } catch (Exception e) {
15       throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);
16     }
17   }
 1 public void parseStatementNode() {
 2     String id = context.getStringAttribute("id");
 3     String databaseId = context.getStringAttribute("databaseId");
 4 
 5     if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
 6       return;
 7     }
 8 
 9     Integer fetchSize = context.getIntAttribute("fetchSize");
10     Integer timeout = context.getIntAttribute("timeout");
11     String parameterMap = context.getStringAttribute("parameterMap");
12     String parameterType = context.getStringAttribute("parameterType");
13     Class<?> parameterTypeClass = resolveClass(parameterType);
14     String resultMap = context.getStringAttribute("resultMap");
15     String resultType = context.getStringAttribute("resultType");
16     String lang = context.getStringAttribute("lang");
17     LanguageDriver langDriver = getLanguageDriver(lang);
18 
19     Class<?> resultTypeClass = resolveClass(resultType);
20     String resultSetType = context.getStringAttribute("resultSetType");
21     StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
22     ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
23 
24     String nodeName = context.getNode().getNodeName();
25     SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
26     boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
27     boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
28     boolean useCache = context.getBooleanAttribute("useCache", isSelect);
29     boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);
30 
31     // Include Fragments before parsing
32     XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
33     includeParser.applyIncludes(context.getNode());
34 
35     // Parse selectKey after includes and remove them.
36     processSelectKeyNodes(id, parameterTypeClass, langDriver);
37     
38     // Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
39     SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
40     String resultSets = context.getStringAttribute("resultSets");
41     String keyProperty = context.getStringAttribute("keyProperty");
42     String keyColumn = context.getStringAttribute("keyColumn");
43     KeyGenerator keyGenerator;
44     String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
45     keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
46     if (configuration.hasKeyGenerator(keyStatementId)) {
47       keyGenerator = configuration.getKeyGenerator(keyStatementId);
48     } else {
49       keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
50           configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
51           ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
52     }
53 
54     builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,  // 跟进此方法
55         fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
56         resultSetTypeEnum, flushCache, useCache, resultOrdered, 
57         keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
58   }
 1   public MappedStatement addMappedStatement(
 2       String id,
 3       SqlSource sqlSource,
 4       StatementType statementType,
 5       SqlCommandType sqlCommandType,
 6       Integer fetchSize,
 7       Integer timeout,
 8       String parameterMap,
 9       Class<?> parameterType,
10       String resultMap,
11       Class<?> resultType,
12       ResultSetType resultSetType,
13       boolean flushCache,
14       boolean useCache,
15       boolean resultOrdered,
16       KeyGenerator keyGenerator,
17       String keyProperty,
18       String keyColumn,
19       String databaseId,
20       LanguageDriver lang,
21       String resultSets) {
22 
23     if (unresolvedCacheRef) {
24       throw new IncompleteElementException("Cache-ref not yet resolved");
25     }
26 
27     id = applyCurrentNamespace(id, false);
28     boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
29 
30     MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType) 
31         .resource(resource)
32         .fetchSize(fetchSize)
33         .timeout(timeout)
34         .statementType(statementType)
35         .keyGenerator(keyGenerator)
36         .keyProperty(keyProperty)
37         .keyColumn(keyColumn)
38         .databaseId(databaseId)
39         .lang(lang)
40         .resultOrdered(resultOrdered)
41         .resultSets(resultSets)
42         .resultMaps(getStatementResultMaps(resultMap, resultType, id))
43         .resultSetType(resultSetType)
44         .flushCacheRequired(valueOrDefault(flushCache, !isSelect))
45         .useCache(valueOrDefault(useCache, isSelect))
46         .cache(currentCache); 
47 
48     ParameterMap statementParameterMap = getStatementParameterMap(parameterMap, parameterType, id);
49     if (statementParameterMap != null) {
50       statementBuilder.parameterMap(statementParameterMap);
51     }
52 
53     MappedStatement statement = statementBuilder.build(); // 构建MappedStatement对象
54     configuration.addMappedStatement(statement);  // 将MappedStatement加入configration对象
55     return statement;
56   }

三、sql执行过程

 

 从测试方法的获取代理对象处跟踪即可...

posted @ 2021-01-15 15:20  佛祖让我来巡山  阅读(95)  评论(0编辑  收藏  举报

佛祖让我来巡山博客站 - 创建于 2018-08-15

开发工程师个人站,内容主要是网站开发方面的技术文章,大部分来自学习或工作,部分来源于网络,希望对大家有所帮助。

Bootstrap中文网