Mybati源码解析

Mybatis入口SqlSessionFactoryBuilder

SqlSessionFactoryBuilder是mybatis的入口,它的作用是解析配置文件,创建SqlSessionFactory,然后由SqlSessionFactory创建SqlSession来执行我们的sql。

SqlSessionFactoryBuilder示例代码

		//创建SqlSessionFactoryBuilder,用于构建SqlSessionFactory
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        //读取配置文件构建SqlSessionFactory
        SqlSessionFactory sessionFactory = builder.build(new FileInputStream("/mybatis-config.xml"));
        //创建SqlSession,用于获取dao接口的代理类或者执行sql
        SqlSession sqlSession = sessionFactory.openSession();
        //获取dao接口的代理类
        MyMapper myMapper = sqlSession.getMapper(MyMapper.class);
        //执行接口方法对应的sql
        myMapper.getById(1);

这段代码的核心就在build方法中,这个方法会读取配置文件并解析,把解析的配置放在Configuration中,比如plugin,解析好的select,insert,delete,update这些标签的内容,及对应的mapper接口等配置。

解析Config配置文件

下面进入到build方法

	public SqlSessionFactory build(InputStream inputStream) {
        return this.build((InputStream)inputStream, (String)null, (Properties)null);
    }


	public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
        SqlSessionFactory var5;
        try {
        	//创建XMLConfigBuilder类,用于解析mybatis配置文件
            XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
            //解析方法在parse
            var5 = this.build(parser.parse());
        } catch (Exception var14) {
            throw ExceptionFactory.wrapException("Error building SqlSession.", var14);
        } finally {
            ErrorContext.instance().reset();

            try {
                inputStream.close();
            } catch (IOException var13) {
            }

        }

        return var5;
    }
    

	public SqlSessionFactory build(Configuration config) {
		//把解析配置文件生成的配置类Configuration作为参数,创建DefaultSqlSessionFactory返回
        return new DefaultSqlSessionFactory(config);
    }

创建解析配置文件类XMLConfigBuilder后,调用parse方法解析配置文件。

	public Configuration parse() {
		//判断是否解析过了,防止重复解析
        if (this.parsed) {
            throw new BuilderException("Each XMLConfigBuilder can only be used once.");
        } else {
        	//标记为已解析
            this.parsed = true;
            //调用parseConfiguration方法
            this.parseConfiguration(this.parser.evalNode("/configuration"));
            return this.configuration;
        }
    }


	
	private void parseConfiguration(XNode root) {
			//这里已经把配置文件的内容解析为XNode对象了
        try {
        	//解析properties标签,并把标签的内容放到Configuration中
            this.propertiesElement(root.evalNode("properties"));
            Properties settings = this.settingsAsProperties(root.evalNode("settings"));
            this.loadCustomVfs(settings);
            this.loadCustomLogImpl(settings);
            this.typeAliasesElement(root.evalNode("typeAliases"));
            //解析plugins标签,把插件类放到Configuration中
            this.pluginElement(root.evalNode("plugins"));
            this.objectFactoryElement(root.evalNode("objectFactory"));
            this.objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
            this.reflectorFactoryElement(root.evalNode("reflectorFactory"));
            this.settingsElement(settings);
            this.environmentsElement(root.evalNode("environments"));
            this.databaseIdProviderElement(root.evalNode("databaseIdProvider"));
            this.typeHandlerElement(root.evalNode("typeHandlers"));
            //(重点)开始解析我们的mapper文件
            this.mapperElement(root.evalNode("mappers"));
        } catch (Exception var3) {
            throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + var3, var3);
        }
    }

解析Mapper文件

解析完其它配置后,下面开始解析我们的mapper文件了

private void mapperElement(XNode parent) throws Exception {
        if (parent != null) {
        	//获取mappers标签的子标签
            Iterator var2 = parent.getChildren().iterator();

            while(true) {
            	//遍历子标签
                while(var2.hasNext()) {
                    XNode child = (XNode)var2.next();
                    String resource;
                    //如果子标签是指定的是包名,那么mybatis会把包下所有的接口添加到Configuration,用于生成代理类。
                    //并解析接口的方法上的注解,例如:@Options,@SelectKey,@SelectProvider,@InsertProvider,@UpdateProvider,@DeleteProvider
                    if ("package".equals(child.getName())) {
                        resource = child.getStringAttribute("name");
                        this.configuration.addMappers(resource);
                    } else {
                    	//子标签是mapper标签,获取mapper标签的resource,url,class属性
                    	//其中resource和url指定的是xml文件, class指定是接口
                        resource = child.getStringAttribute("resource");
                        String url = child.getStringAttribute("url");
                        String mapperClass = child.getStringAttribute("class");
                        XMLMapperBuilder mapperParser;
                        InputStream inputStream;
                       
                        if (resource != null && url == null && mapperClass == null) {
                        	//如果resource不为空,url和mapperClass都为空,那么就创建解析mapper为xml文件的类XMLMapperBuilder
                            ErrorContext.instance().resource(resource);
                            inputStream = Resources.getResourceAsStream(resource);
                            mapperParser = new XMLMapperBuilder(inputStream, this.configuration, resource, this.configuration.getSqlFragments());
                            mapperParser.parse();
                        } else if (resource == null && url != null && mapperClass == null) {
                        	//如果url不为空,resource和mapperClass都为空,那么就创建解析mapper为xml文件的类XMLMapperBuilder
                            ErrorContext.instance().resource(url);
                            inputStream = Resources.getUrlAsStream(url);
                            mapperParser = new XMLMapperBuilder(inputStream, this.configuration, url, this.configuration.getSqlFragments());
                            mapperParser.parse();
                        } else {
                        	
                            if (resource != null || url != null || mapperClass == null) {
                                throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
                            }
							//如果mapperClass不为空,resource和url都为空,说明mapper是接口,就调用解析接口的方法
                            Class<?> mapperInterface = Resources.classForName(mapperClass);
                            this.configuration.addMapper(mapperInterface);
                        }
                    }
                }

                return;
            }
        }
    }

上面是对mapper的配置方式进行判断,如果是xml文件的方式,那么就调用XMLMapperBuilder.parse()进行解析,如果是接口的方式,那么就调用Configuration.addMapper()方法解析。

那么这里我们以解析mapper为xml文件的方式来进行分析。

	public void parse() {
		//判断mapper文件是否被解析过
        if (!this.configuration.isResourceLoaded(this.resource)) {
        	//没有被解析过,则开始解析
            this.configurationElement(this.parser.evalNode("/mapper"));
            //把mapper文件添加到Configuration中,表示已解析
            this.configuration.addLoadedResource(this.resource);
            //把mapper文件的namespace属性的接口添加到Configuration
            this.bindMapperForNamespace();
        }

        this.parsePendingResultMaps();
        this.parsePendingCacheRefs();
        this.parsePendingStatements();
    }

进入到configurationElement方法

	private void configurationElement(XNode context) {
        try {
            String namespace = context.getStringAttribute("namespace");
            if (namespace != null && !namespace.equals("")) {
                this.builderAssistant.setCurrentNamespace(namespace);
                this.cacheRefElement(context.evalNode("cache-ref"));
                this.cacheElement(context.evalNode("cache"));
                this.parameterMapElement(context.evalNodes("/mapper/parameterMap"));
                this.resultMapElements(context.evalNodes("/mapper/resultMap"));
                this.sqlElement(context.evalNodes("/mapper/sql"));
                //解析select,insert,update,delete标签
                this.buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
            } else {
                throw new BuilderException("Mapper's namespace cannot be empty");
            }
        } catch (Exception var3) {
            throw new BuilderException("Error parsing Mapper XML. The XML location is '" + this.resource + "'. Cause: " + var3, var3);
        }
    }

解析select,insert,update,delete标签

buildStatementFromContext()

	private void buildStatementFromContext(List<XNode> list) {
        if (this.configuration.getDatabaseId() != null) {
            this.buildStatementFromContext(list, this.configuration.getDatabaseId());
        }

        this.buildStatementFromContext(list, (String)null);
    }


	private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
        Iterator var3 = list.iterator();
		//遍历select,insert,update,delete标签
        while(var3.hasNext()) {
            XNode context = (XNode)var3.next();
            //创建XMLStatementBuilder,用来解析这些标签
            XMLStatementBuilder statementParser = new XMLStatementBuilder(this.configuration, this.builderAssistant, context, requiredDatabaseId);

            try {
            	//解析
                statementParser.parseStatementNode();
            } catch (IncompleteElementException var7) {
                this.configuration.addIncompleteStatement(statementParser);
            }
        }

    }

parseStatementNode()

public void parseStatementNode() {
		//获取标签的id属性
        String id = this.context.getStringAttribute("id");
        String databaseId = this.context.getStringAttribute("databaseId");
        if (this.databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
            String nodeName = this.context.getNode().getNodeName();
            SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
            boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
            boolean flushCache = this.context.getBooleanAttribute("flushCache", !isSelect);
            boolean useCache = this.context.getBooleanAttribute("useCache", isSelect);
            boolean resultOrdered = this.context.getBooleanAttribute("resultOrdered", false);
            XMLIncludeTransformer includeParser = new XMLIncludeTransformer(this.configuration, this.builderAssistant);
            includeParser.applyIncludes(this.context.getNode());
            String parameterType = this.context.getStringAttribute("parameterType");
            Class<?> parameterTypeClass = this.resolveClass(parameterType);
            String lang = this.context.getStringAttribute("lang");
            LanguageDriver langDriver = this.getLanguageDriver(lang);
            this.processSelectKeyNodes(id, parameterTypeClass, langDriver);
            String keyStatementId = id + "!selectKey";
            keyStatementId = this.builderAssistant.applyCurrentNamespace(keyStatementId, true);
            Object keyGenerator;
            if (this.configuration.hasKeyGenerator(keyStatementId)) {
                keyGenerator = this.configuration.getKeyGenerator(keyStatementId);
            } else {
                keyGenerator = this.context.getBooleanAttribute("useGeneratedKeys", this.configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType)) ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
            }
			//获取SqlSource,用于获取sql语句
            SqlSource sqlSource = langDriver.createSqlSource(this.configuration, this.context, parameterTypeClass);
            StatementType statementType = StatementType.valueOf(this.context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
            Integer fetchSize = this.context.getIntAttribute("fetchSize");
            Integer timeout = this.context.getIntAttribute("timeout");
            String parameterMap = this.context.getStringAttribute("parameterMap");
            String resultType = this.context.getStringAttribute("resultType");
            Class<?> resultTypeClass = this.resolveClass(resultType);
            String resultMap = this.context.getStringAttribute("resultMap");
            String resultSetType = this.context.getStringAttribute("resultSetType");
            ResultSetType resultSetTypeEnum = this.resolveResultSetType(resultSetType);
            if (resultSetTypeEnum == null) {
                resultSetTypeEnum = this.configuration.getDefaultResultSetType();
            }

            String keyProperty = this.context.getStringAttribute("keyProperty");
            String keyColumn = this.context.getStringAttribute("keyColumn");
            String resultSets = this.context.getStringAttribute("resultSets");
            //把标签转换成MappedStatement,最后放到Configuration中
            this.builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum, flushCache, useCache, resultOrdered, (KeyGenerator)keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
        }
    }

到这里mybatis的解析阶段就分析完成了。

Mapper接口代理类的生成

在上面的示例代码中,我们只需通过SqlSession.getMapper()方法就能获取到代理类,然后调用代理类的方法就能执行对应的sql,拿到返回值,那么这是怎么实现的呢?
1:先看mapper接口是如何添加到Configuration中的
入口位于Configuration的addMapper方法

	public <T> void addMapper(Class<T> type) {
		//MapperRegistry是用来保存mapper接口的一个类
        this.mapperRegistry.addMapper(type);
    }

下面看MapperRegistry的addMapper方法

	public <T> void addMapper(Class<T> type) {
		//判断参数是否是一个接口
        if (type.isInterface()) {
            if (this.hasMapper(type)) {
                throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
            }

            boolean loadCompleted = false;

            try {
            	//把mapper封装为MapperProxyFactory,用于生成代理类,并放到knownMappers这个map中
                this.knownMappers.put(type, new MapperProxyFactory(type));
                //用于解析接口上的@Select,@Insert,@Update等注解
                MapperAnnotationBuilder parser = new MapperAnnotationBuilder(this.config, type);
                parser.parse();
                loadCompleted = true;
            } finally {
                if (!loadCompleted) {
                    this.knownMappers.remove(type);
                }

            }
        }

    }

2:如何获取mapper接口及代理类
入口位于SqlSession的getMapper方法

	public <T> T getMapper(Class<T> type) {
        return this.configuration.getMapper(type, this);
    }

紧接着调用了Configuration的getMapper方法

	public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
        return this.mapperRegistry.getMapper(type, sqlSession);
    }

由调了MapperRegistry的getMapper方法

	public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
		//这里获取了之前保存的MapperProxyFactory
        MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
        if (mapperProxyFactory == null) {
            throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
        } else {
            try {
            	//生成代理类
                return mapperProxyFactory.newInstance(sqlSession);
            } catch (Exception var5) {
                throw new BindingException("Error getting mapper instance. Cause: " + var5, var5);
            }
        }
    }

生成代理类:位于MapperProxyFactory的newInstance方法

	public T newInstance(SqlSession sqlSession) {
        MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
        return this.newInstance(mapperProxy);
    }


	protected T newInstance(MapperProxy<T> mapperProxy) {
        return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
    }

到这里代理类生成完成,当我们调用代理类的方法时,就会进入到MapperProxy的invoke方法,然后根据mapper的类名+方法名找到对应的MappedStatement,最后由Executor去执行sql。

关于Environment

在mybatis解析配置文件时,会根据我们对环境的配置进行解析,创建事务工厂(TransactionFactory)和数据源(DataSource)对象,然后放到Environment这个类中,在我们获取SqlSession时会用到。
这个环境对象包含了环境标识id,事物工厂以及数据源。
mybatis支持配置多个不同的环境,比如dev,test,pro环境。
下面是Environment的代码片段

public final class Environment {
    private final String id;
    private final TransactionFactory transactionFactory;
    private final DataSource dataSource;

    public Environment(String id, TransactionFactory transactionFactory, DataSource dataSource) {
        if (id == null) {
            throw new IllegalArgumentException("Parameter 'id' must not be null");
        } else if (transactionFactory == null) {
            throw new IllegalArgumentException("Parameter 'transactionFactory' must not be null");
        } else {
            this.id = id;
            if (dataSource == null) {
                throw new IllegalArgumentException("Parameter 'dataSource' must not be null");
            } else {
                this.transactionFactory = transactionFactory;
                this.dataSource = dataSource;
            }
        }
    }
}

关于Configuration

Configuration是用来保存解析好的配置信息的,下面是Configuration的代码片段

public class Configuration {
    protected Environment environment;
    protected boolean safeRowBoundsEnabled;
    protected boolean safeResultHandlerEnabled;
    protected boolean mapUnderscoreToCamelCase;
    protected boolean aggressiveLazyLoading;
    protected boolean multipleResultSetsEnabled;
    protected boolean useGeneratedKeys;
    protected boolean useColumnLabel;
    protected boolean cacheEnabled;
    protected boolean callSettersOnNulls;
    protected boolean useActualParamName;
    protected boolean returnInstanceForEmptyRow;
    protected String logPrefix;
    protected Class<? extends Log> logImpl;
    protected Class<? extends VFS> vfsImpl;
    protected LocalCacheScope localCacheScope;
    protected JdbcType jdbcTypeForNull;
    protected Set<String> lazyLoadTriggerMethods;
    protected Integer defaultStatementTimeout;
    protected Integer defaultFetchSize;
    protected ResultSetType defaultResultSetType;
    protected ExecutorType defaultExecutorType;
    protected AutoMappingBehavior autoMappingBehavior;
    protected AutoMappingUnknownColumnBehavior autoMappingUnknownColumnBehavior;
    protected Properties variables;
    protected ReflectorFactory reflectorFactory;
    protected ObjectFactory objectFactory;
    protected ObjectWrapperFactory objectWrapperFactory;
    protected boolean lazyLoadingEnabled;
    protected ProxyFactory proxyFactory;
    protected String databaseId;
    protected Class<?> configurationFactory;
    //保存mapper接口的类,用于生成代理类
    protected final MapperRegistry mapperRegistry;
    //拦截器链,用来保存我们配置的插件
    protected final InterceptorChain interceptorChain;
    protected final TypeHandlerRegistry typeHandlerRegistry;
    protected final TypeAliasRegistry typeAliasRegistry;
    protected final LanguageDriverRegistry languageRegistry;
    //mybatis会把解析好的select,insert,delete,update标签封装成MappedStatement,
    //然后放到这里,这里key是mapper文件的namespace+标签的id
    protected final Map<String, MappedStatement> mappedStatements;
    protected final Map<String, Cache> caches;
    protected final Map<String, ResultMap> resultMaps;
    protected final Map<String, ParameterMap> parameterMaps;
    protected final Map<String, KeyGenerator> keyGenerators;
    protected final Set<String> loadedResources;
    protected final Map<String, XNode> sqlFragments;
    protected final Collection<XMLStatementBuilder> incompleteStatements;
    protected final Collection<CacheRefResolver> incompleteCacheRefs;
    protected final Collection<ResultMapResolver> incompleteResultMaps;
    protected final Collection<MethodResolver> incompleteMethods;
    protected final Map<String, String> cacheRefMap;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值