Mybatis源码解析

2021/6/6 14:23:04

本文主要是介绍Mybatis源码解析,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

Mybatis源码解析

首先是Mybatis的基本使用步骤

public class StudentMapperTest {
    private static SqlSessionFactory sqlSessionFactory;

    @BeforeClass
    public static void init() {
        try {
            Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
            //内部实际创建了一个Configuration对象,并以此创建出DefaultSqlSessionFactory对象
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Test
    public void testSelectList() {
        SqlSession sqlSession = null;
        try {
            sqlSession = sqlSessionFactory.openSession();
			//这里实际上是通过executor对象调用方法
            List<Student> students = sqlSession.selectList("com.demo.mapper.UserMapper.getUserByName");
            //这里实际上是通过this.configuration.getMapper(type, this);来获取代理对象的。这个代理对象内部是通过sqlSession执行sql。
            //而sqlSession内部实际上都是通过executor执行的。而一级缓存又在executor中所以,所有通过同一个sqlSession执行的方法共享一级缓存。
            //sqlSessionFactory.openSession()如果再创建一个sqlSession,实际上内部的executor也是新创建的所以就不会与先前那个sqlSession共享一级缓存了。
            Mapper mapper = sqlSession.getMapper(mapper.class);
            for (int i = 0; i < students.size(); i++) {
                System.out.println(students.get(i));
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (sqlSession != null) {
                sqlSession.close();
            }
        }
    }
}

这里是具体的配置文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<!-- 通过这个配置文件完成mybatis与数据库的连接 -->
<configuration>
    <!-- 引入数据源配置 database.properties 文件 -->
    <properties resource="database.properties"></properties>

    <!--配置mybatis 运行中的一些行为 -->
    <settings>
        <!-- 设置Mybatis的log实现为LOG4J -->
        <setting name="logImpl" value="LOG4J"/>
    </settings>

    <typeAliases>
        <!-- 
        <typeAlias alias="User" type="com.zy.entity.User"/>
         -->
        <package name="cn.zy.entity"/>
    </typeAliases>

    <!-- 配置mybatis运行环境 -->
    <environments default="dev">
        <environment id="dev">
            <!-- 采用jdbc事务管理 -->
            <transactionManager type="JDBC"></transactionManager>
            <!-- 采用Mybatis自带的数据源 POOLED -->
            <dataSource type="POOLED">
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${user}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>

    <!-- 将mapper文件加入到配置文件中 -->
    <mappers>
        <mapper resource="cn/zy/dao/UserMapper.xml"/>
        <package name="com.hytc.mapper" />
        <package url="....." />
        <package class="接口路径"/>
    </mappers>
   
</configuration>

先放上通过sqlSession执行sql语句的过程这里是引用Coder648的博客

//此方法在SimpleExecutor的父类BaseExecutor中实现
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
	//根据传入的参数动态获得SQL语句,最后返回用BoundSql对象表示
    BoundSql boundSql = ms.getBoundSql(parameter);
    //为本次查询创建缓存的Key
    CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
    return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
 }
 
//进入query的重载方法中
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
    if (closed) {
      throw new ExecutorException("Executor was closed.");
    }
    if (queryStack == 0 && ms.isFlushCacheRequired()) {
      clearLocalCache();
    }
    List<E> list;
    try {
      queryStack++;
      list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
      if (list != null) {
        handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
      } else {
      	// 如果缓存中没有本次查找的值,那么从数据库中查询
        list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
      }
    } finally {
      queryStack--;
    }
    if (queryStack == 0) {
      for (DeferredLoad deferredLoad : deferredLoads) {
        deferredLoad.load();
      }
      // issue #601
      deferredLoads.clear();
      if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
        // issue #482
        clearLocalCache();
      }
    }
    return list;
  }

//从数据库查询
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    List<E> list;
    localCache.putObject(key, EXECUTION_PLACEHOLDER);
    try {
      // 查询的方法
      list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
    } finally {
      localCache.removeObject(key);
    }
    // 将查询结果放入缓存
    localCache.putObject(key, list);
    if (ms.getStatementType() == StatementType.CALLABLE) {
      localOutputParameterCache.putObject(key, parameter);
    }
    return list;
  }

// SimpleExecutor中实现父类的doQuery抽象方法
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    Statement stmt = null;
    try {
      Configuration configuration = ms.getConfiguration();
      // 传入参数创建StatementHanlder对象来执行查询
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
      // 创建jdbc中的statement对象
      stmt = prepareStatement(handler, ms.getStatementLog());
      // StatementHandler进行处理
      return handler.query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }

// 创建Statement的方法
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    //条代码中的getConnection方法经过重重调用最后会调用openConnection方法,从连接池中获得连接。
    Connection connection = getConnection(statementLog);
    stmt = handler.prepare(connection, transaction.getTimeout());
    handler.parameterize(stmt);
    return stmt;
  }
//从连接池获得连接的方法
protected void openConnection() throws SQLException {
    if (log.isDebugEnabled()) {
      log.debug("Opening JDBC Connection");
    }
    //从连接池获得连接
    connection = dataSource.getConnection();
    if (level != null) {
      connection.setTransactionIsolation(level.getLevel());
    }
    setDesiredAutoCommit(autoCommit);
  }


//进入StatementHandler进行处理的query,StatementHandler中默认的是PreparedStatementHandler
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    //原生jdbc的执行
    ps.execute();
    //处理结果返回。
    return resultSetHandler.handleResultSets(ps);
  }

我们来根据基本使用来看一下源码

第一步

Reader reader = Resources.getResourceAsReader("mybatis-config.xml");

获取mybatis配置文件的字节流对象

第二步

sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);

获取sqlSessionFactory对象,实际上这是一个接口,我们获取到的对象实际上是该接口的实现类DefaultSqlSessionFactory。

SqlSessionFactoryBuilder有关源码如下

build方法的参数
1、properties 实际上这是一个hashTable,存放了外部数据库连接池的位置信息。可以当参数传入,也可以在配置文件配置,因为最终将二者存放在一个default properties 中。作用是给数据库连接池提供配置数据(url username password)
2、reader读取mybatis配置文件的文件流对象 这是主要的
3、environment,这个参数的作用是当mybatis配置了多个数据源时,指定使用哪一个。

public class SqlSessionFactoryBuilder {
    public SqlSessionFactoryBuilder() {
    }
    
	//从这可以看出build大体分为两类,主要是字符流,以及字符流。
    public SqlSessionFactory build(Reader reader) {
        return this.build((Reader)reader, (String)null, (Properties)null);
    }

    public SqlSessionFactory build(Reader reader, String environment) {
        return this.build((Reader)reader, environment, (Properties)null);
    }

    public SqlSessionFactory build(Reader reader, Properties properties) {
        return this.build((Reader)reader, (String)null, properties);
    }

    public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
        SqlSessionFactory var5;
        try {
        	//parser创建出Configuration,并根据配置文件对其初始化
            XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
            //var5就是调用build最终返回的对象(看最下面的方法),也就是DefaultSqlSessionFactory
            var5 = this.build(parser.parse()传入一个Configuration);
        } 
        ....省略若干
        return var5;
    }

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

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

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

    public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
        SqlSessionFactory var5;
        try {
            XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
            var5 = this.build(parser.parse());
        } 
		....省略若干
        return var5;
    }

    public SqlSessionFactory build(Configuration config) {
        return new DefaultSqlSessionFactory(config);
    }
}

接下来是XMLConfigBuilder的源码,省略若干

public class XMLConfigBuilder extends BaseBuilder {
    private boolean parsed;
    private final XPathParser parser;
    private String environment;
    private final ReflectorFactory localReflectorFactory;

    public XMLConfigBuilder(Reader reader) {
        this((Reader)reader, (String)null, (Properties)null);
    }

    public XMLConfigBuilder(Reader reader, String environment) {
        this((Reader)reader, environment, (Properties)null);
    }

    public XMLConfigBuilder(Reader reader, String environment, Properties props) {
    	//XPathParser是用来解析xml配置文件的
        this(new XPathParser(reader, true, props, new XMLMapperEntityResolver()), environment, props);
    }

    public XMLConfigBuilder(InputStream inputStream) {
        this((InputStream)inputStream, (String)null, (Properties)null);
    }

    public XMLConfigBuilder(InputStream inputStream, String environment) {
        this((InputStream)inputStream, environment, (Properties)null);
    }

    public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
        this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
    }

    private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
    	//首先创建了Configuration对象。并传给了它的父类,同时也是parse返回的对象
        super(new Configuration());
        this.localReflectorFactory = new DefaultReflectorFactory();
        ErrorContext.instance().resource("SQL Mapper Configuration");
        this.configuration.setVariables(props);
        this.parsed = false;
        this.environment = environment;
        this.parser = parser;
    }

    public Configuration parse() {
    	//这里的parsed是全局变量,不允许对configuration进行多次初始化
        if (this.parsed) {
            throw new BuilderException("Each XMLConfigBuilder can only be used once.");
        } else {
            this.parsed = true;
   			//这个方法实际上是xml配置文件每个标签对解析成以一个节点对象,并对configuration进行初始化
            this.parseConfiguration(this.parser.evalNode("/configuration"));
            //最终返回从父类继承过来的configuration对象
            return this.configuration;
        }
    }
	//这里是具体的方法,下列的方法中大部分通过这种形式this.configuration.setVfsImpl(vfsImpl)给configuration初始化。
    private void parseConfiguration(XNode root) {
        try {
            this.propertiesElement(root.evalNode("properties"));
            Properties settings = this.settingsAsProperties(root.evalNode("settings"));
            this.loadCustomVfs(settings);
            this.loadCustomLogImpl(settings);
            this.typeAliasesElement(root.evalNode("typeAliases"));
            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"));
            this.mapperElement(root.evalNode("mappers"));
        } catch (Exception var3) {
            throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + var3, var3);
        }
    }

    
    private void loadCustomVfs(Properties props) {
        this.configuration.setVfsImpl(vfsImpl);
    }

    private void loadCustomLogImpl(Properties props) { 
        this.configuration.setLogImpl(logImpl);
    }

    private void typeAliasesElement(XNode parent) {
       this.configuration.getTypeAliasRegistry().registerAliases(alias);
    }

    private void pluginElement(XNode parent) throws Exception {
 		 this.configuration.addInterceptor(interceptorInstance);
    }

    private void objectFactoryElement(XNode context) throws Exception {						     		this.configuration.setObjectFactory(factory) 
    }

    private void objectWrapperFactoryElement(XNode context) throws Exception {
        this.configuration.setObjectWrapperFactory(factory);
    }

    private void reflectorFactoryElement(XNode context) throws Exception {
       this.configuration.setReflectorFactory(factory);
    }

    private void propertiesElement(XNode context) throws Exception {
    	if (context != null) {
            Properties defaults = context.getChildrenAsProperties();
            String resource = context.getStringAttribute("resource");
            String url = context.getStringAttribute("url");
            if (resource != null && url != null) {
                throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference.  Please specify one or the other.");
            }

            if (resource != null) {
                defaults.putAll(Resources.getResourceAsProperties(resource));
            } else if (url != null) {
            	//这一步是解析资源,返回一个propertise对象,并放进defaults
                defaults.putAll(Resources.getUrlAsProperties(url));
            }
			//这里是获取build传入的Properties,因为前面设置了this.configuration.setVariables(props);
            Properties vars = this.configuration.getVariables();
            //如果不为空就合并。
            if (vars != null) {
                defaults.putAll(vars);
            }
            //设置的作用是提供将${user}替换成真实值的数据。
            this.parser.setVariables(defaults);
            //将最终合并后Properties给configuration赋值。注意Properties实际上是一个hashTable存放的连接数据库的配置
            this.configuration.setVariables(defaults);
        }

    }

    private void settingsElement(Properties props) {
        this.configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL")));
        this.configuration.setAutoMappingUnknownColumnBehavior(AutoMappingUnknownColumnBehavior.valueOf(props.getProperty("autoMappingUnknownColumnBehavior", "NONE")));
        //开起二级缓存的配置
        this.configuration.setCacheEnabled(this.booleanValueOf(props.getProperty("cacheEnabled"), true));
        this.configuration.setProxyFactory((ProxyFactory)this.createInstance(props.getProperty("proxyFactory")));
        this.configuration.setLazyLoadingEnabled(this.booleanValueOf(props.getProperty("lazyLoadingEnabled"), false));
        this.configuration.setAggressiveLazyLoading(this.booleanValueOf(props.getProperty("aggressiveLazyLoading"), false));
        this.configuration.setMultipleResultSetsEnabled(this.booleanValueOf(props.getProperty("multipleResultSetsEnabled"), true));
        this.configuration.setUseColumnLabel(this.booleanValueOf(props.getProperty("useColumnLabel"), true));
        this.configuration.setUseGeneratedKeys(this.booleanValueOf(props.getProperty("useGeneratedKeys"), false));
        this.configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE")));
        this.configuration.setDefaultStatementTimeout(this.integerValueOf(props.getProperty("defaultStatementTimeout"), (Integer)null));
        this.configuration.setDefaultFetchSize(this.integerValueOf(props.getProperty("defaultFetchSize"), (Integer)null));
        this.configuration.setDefaultResultSetType(this.resolveResultSetType(props.getProperty("defaultResultSetType")));
        this.configuration.setMapUnderscoreToCamelCase(this.booleanValueOf(props.getProperty("mapUnderscoreToCamelCase"), false));
        this.configuration.setSafeRowBoundsEnabled(this.booleanValueOf(props.getProperty("safeRowBoundsEnabled"), false));
        this.configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope", "SESSION")));
        this.configuration.setJdbcTypeForNull(JdbcType.valueOf(props.getProperty("jdbcTypeForNull", "OTHER")));
        this.configuration.setLazyLoadTriggerMethods(this.stringSetValueOf(props.getProperty("lazyLoadTriggerMethods"), "equals,clone,hashCode,toString"));
        this.configuration.setSafeResultHandlerEnabled(this.booleanValueOf(props.getProperty("safeResultHandlerEnabled"), true));
        this.configuration.setDefaultScriptingLanguage(this.resolveClass(props.getProperty("defaultScriptingLanguage")));
        this.configuration.setDefaultEnumTypeHandler(this.resolveClass(props.getProperty("defaultEnumTypeHandler")));
        this.configuration.setCallSettersOnNulls(this.booleanValueOf(props.getProperty("callSettersOnNulls"), false));
        this.configuration.setUseActualParamName(this.booleanValueOf(props.getProperty("useActualParamName"), true));
        this.configuration.setReturnInstanceForEmptyRow(this.booleanValueOf(props.getProperty("returnInstanceForEmptyRow"), false));
        this.configuration.setLogPrefix(props.getProperty("logPrefix"));
        this.configuration.setConfigurationFactory(this.resolveClass(props.getProperty("configurationFactory")));
    }

    private void environmentsElement(XNode context) throws Exception {
          if (context != null) {
          	//这里就是判断builed传过来的String environment参数了,如果不传使用默认数据源。当然这个默认的也是配置文件中指定的
            if (this.environment == null) {
                this.environment = context.getStringAttribute("default");
            }

            Iterator var2 = context.getChildren().iterator();
			//这里是遍历所有的数据源,找到与environment相等名字的数据源
            while(var2.hasNext()) {
                XNode child = (XNode)var2.next();
                String id = child.getStringAttribute("id");
                if (this.isSpecifiedEnvironment(id)) {
                    TransactionFactory txFactory = this.transactionManagerElement(child.evalNode("transactionManager"));
                    //创建数据库连接池工厂对象。
                    DataSourceFactory dsFactory = this.dataSourceElement(child.evalNode("dataSource"));
                    DataSource dataSource = dsFactory.getDataSource();
                    //级联调用,实际上是返回键id,dataSource,txFactory封装在一起的一个对象
                    Builder environmentBuilder = (new Builder(id)).transactionFactory(txFactory).dataSource(dataSource);
                  
                    
                   //environmentBuilder.build()方法将上述三个参数作为environment的有参构造参数创建environment对象,environment其实也是对这三个参数的一个封装
                    this.configuration.setEnvironment(environmentBuilder.build());
                }
            }
        }
    }
    //创建连接池工厂
    private DataSourceFactory dataSourceElement(XNode context) throws Exception {
        if (context != null) {
            String type = context.getStringAttribute("type");
            //这一步获取连接数据库的信息等等
            Properties props = context.getChildrenAsProperties();
            DataSourceFactory factory = (DataSourceFactory)this.resolveClass(type).newInstance();
            factory.setProperties(props);
            return factory;
        } else {
            throw new BuilderException("Environment declaration requires a DataSourceFactory.");
        }
    }

    private void databaseIdProviderElement(XNode context) {
            this.configuration.setDatabaseId(databaseId);
    }
    private void mapperElement(XNode parent) throws Exception {
         if (parent != null) {
            Iterator var2 = parent.getChildren().iterator();

            while(true) {
                while(var2.hasNext()) {
                    XNode child = (XNode)var2.next();
                    String resource;
                    //在这mappers分为两种,package分一种,其他url class resource分一种
                    if ("package".equals(child.getName())) {
                        resource = child.getStringAttribute("name");
                        this.configuration.addMappers(resource);
                    } else {
                        resource = child.getStringAttribute("resource");
                        String url = child.getStringAttribute("url");
                        String mapperClass = child.getStringAttribute("class");
                        XMLMapperBuilder mapperParser;
                        InputStream inputStream;
                        //从这可以看出对于每一个 mapper url class resource只能选择一种
                        if (resource != null && url == null && mapperClass == null) {
                            ErrorContext.instance().resource(resource);
                            inputStream = Resources.getResourceAsStream(resource);
                            //这里是对mapper.xml进行解析同解析mybatis配置文件类似
                            mapperParser = new XMLMapperBuilder(inputStream, this.configuration, resource, this.configuration.getSqlFragments());
                            mapperParser.parse();
                        } else if (resource == null && url != null && mapperClass == null) {
                            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.");
                            }
							//无论是什么方式最终都会将mapperInterface通过addMapper创建实现类对象,上面的也会调用this.configuration.addMapper()方法,存进configuration的属性mapperRegistry的一个map集合中Map<Class<?>, MapperProxyFactory<?>>集合中。而这个集合中的value是mapper的代理工厂,用来创建mapper的代理对象。在这里只是创建工厂,当调用getMapper是代理对象才被创建出来
                            Class<?> mapperInterface = Resources.classForName(mapperClass);
                            this.configuration.addMapper(mapperInterface);
                        }
                    }
                }

                return;
            }
        }
    }
}

这里是对mapper操作的方法

public <T> void addMapper(Class<T> type) {
        this.mapperRegistry.addMapper(type);
}
//这个MapperRegistry也是Configuration的一个成员变量
public class MapperRegistry {
    private final Configuration config;
    //this.mapperRegistry.addMapper(type)就是存放进knownMappers这个map中
    private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap();

    public MapperRegistry(Configuration config) {
        this.config = config;
    }
	//这个getMapper是返回一个Mapper的代理对象
    public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    	//从knownMappers中获取,它的值是调用addMapper方法赋予的。而这个方法的调用是Configuration初始化时执行的。
        MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
        if (mapperProxyFactory == null) {
            throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
        } else {
            try {
            	//详细查看这个方法,这个方法创建了mapper代理对象
                return mapperProxyFactory.newInstance(sqlSession);
            } catch (Exception var5) {
                throw new BindingException("Error getting mapper instance. Cause: " + var5, var5);
            }
        }
    }

    public <T> boolean hasMapper(Class<T> type) {
        return this.knownMappers.containsKey(type);
    }

    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 {
                this.knownMappers.put(type, new MapperProxyFactory(type));
                MapperAnnotationBuilder parser = new MapperAnnotationBuilder(this.config, type);
                parser.parse();
                loadCompleted = true;
            } finally {
                if (!loadCompleted) {
                    this.knownMappers.remove(type);
                }

            }
        }

    }

    public Collection<Class<?>> getMappers() {
        return Collections.unmodifiableCollection(this.knownMappers.keySet());
    }
	//扫描包中的class加入knownMappers map集合中
    public void addMappers(String packageName, Class<?> superType) {
        ResolverUtil<Class<?>> resolverUtil = new ResolverUtil();
        //这个函数是扫描包中所有的class并且将类型为new IsA(superType)加进mapperSet中,就是下面返回的set集合
        resolverUtil.find(new IsA(superType), packageName);
        Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses();
        Iterator var5 = mapperSet.iterator();

        while(var5.hasNext()) {
            Class<?> mapperClass = (Class)var5.next();
            this.addMapper(mapperClass);
        }

    }

    public void addMappers(String packageName) {
        this.addMappers(packageName, Object.class);
    }
}

mapperProxyFactory

public class MapperProxyFactory<T> {
    private final Class<T> mapperInterface;
    private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap();

    public MapperProxyFactory(Class<T> mapperInterface) {
        this.mapperInterface = mapperInterface;
    }

    public Class<T> getMapperInterface() {
        return this.mapperInterface;
    }

    public Map<Method, MapperMethod> getMethodCache() {
        return this.methodCache;
    }

    protected T newInstance(MapperProxy<T> mapperProxy) {
        return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
    }
	//为什么要传入sqlsession呢?实际上通过mapper代理对象执行的sql语句实际上是通过sqlsession来执行的,
    public T newInstance(SqlSession sqlSession) {
        MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
        return this.newInstance(mapperProxy);
    }
}

MapperProxy

public class MapperProxy<T> implements InvocationHandler, Serializable {
    private static final long serialVersionUID = -6424540398559729838L;
    private final SqlSession sqlSession;
    private final Class<T> mapperInterface;
    private final Map<Method, MapperMethod> methodCache;

    public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
        this.sqlSession = sqlSession;
        this.mapperInterface = mapperInterface;
        this.methodCache = methodCache;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
        	//直接放行Object定义的方法
            if (Object.class.equals(method.getDeclaringClass())) {
                return method.invoke(this, args);
            }
			//直接放行默认方法。默认方法是接口中定义一个已实现方法,且该接口的实现类不需要实现该方法。为什么要有默认方法。因为如果没有默认方法,加入给JDK中的某个接口添加一个新的抽象方法,那么所有实现了该接口的类都得修改,影响将非常大。
            if (method.isDefault()) {
                return this.invokeDefaultMethod(proxy, method, args);
            }
        } catch (Throwable var5) {
            throw ExceptionUtil.unwrapThrowable(var5);
        }
		//创建出mapperMethod对象,这个对象内部通过调用sqlSession执行sql语句,同时MapperMethod内内有一个属性command,这个command对象封装了一个内部通过从configuration对象中的mappedStatements.get(mapperInterface.getName() + "." + methodName)获取mappedStatement。
		//然后获取mappedStatement的id="com.demo.mapper.UserMapper.getUserByName"赋值给command。用来执行sqlSession.方法("com.demo.mapper.UserMapper.getUserByName",params)
        MapperMethod mapperMethod = this.cachedMapperMethod(method);
        return mapperMethod.execute(this.sqlSession, args);
    }

    private MapperMethod cachedMapperMethod(Method method) {
        return (MapperMethod)this.methodCache.computeIfAbsent(method, (k) -> {
            return new MapperMethod(this.mapperInterface, method, this.sqlSession.getConfiguration());
        });
    }

    private Object invokeDefaultMethod(Object proxy, Method method, Object[] args) throws Throwable {
        Constructor<Lookup> constructor = Lookup.class.getDeclaredConstructor(Class.class, Integer.TYPE);
        if (!constructor.isAccessible()) {
            constructor.setAccessible(true);
        }

        Class<?> declaringClass = method.getDeclaringClass();
        return ((Lookup)constructor.newInstance(declaringClass, 15)).unreflectSpecial(method, declaringClass).bindTo(proxy).invokeWithArguments(args);
    }
}

以上都是通过读取mybatis配置文件创建Configuration对象,并为Configuration对象初始化的一个过程,主要发生在XMLConfigBuilder方法中,重点是通过读取配置信息创建了environment对象,这个对象封装了我们指定的数据库连接池对象,事务工厂对象,id。将mapper代理工厂对象存储在 MapperRegistry的knownMappers集合中

创建出Configuration对象后通过这个对象创建出new DefaultSqlSessionFactory(config)对象,再获取SqlSession对象,注意SqlSession中是封装了Configuration对象的

    private SqlSession openSessionFromConnection(ExecutorType execType, Connection connection) {
        DefaultSqlSession var8;
        try {
            boolean autoCommit;
            try {
                autoCommit = connection.getAutoCommit();
            } catch (SQLException var13) {
                autoCommit = true;
            }

            Environment environment = this.configuration.getEnvironment();
            TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment);
            Transaction tx = transactionFactory.newTransaction(connection);
            //configuration.newExecutor源码如下,配合下面一张图
            Executor executor = this.configuration.newExecutor(tx, execType);
            var8 = new DefaultSqlSession(this.configuration, executor, autoCommit);
        } catch (Exception var14) {
            throw ExceptionFactory.wrapException("Error opening session.  Cause: " + var14, var14);
        } finally {
            ErrorContext.instance().reset();
        }

        return var8;
    }

    private TransactionFactory getTransactionFactoryFromEnvironment(Environment environment) {
        return (TransactionFactory)(environment != null && environment.getTransactionFactory() != null ? environment.getTransactionFactory() : new ManagedTransactionFactory());
    }

这里是Executor的继承体系
在这里插入图片描述
最底层的接口是Executor,有两个实现类:BaseExecutor和CachingExecutor,CachingExecutor用于二级缓存,而BaseExecutor则用于一级缓存及基础的操作。

1、SimpleExecutor是最简单的执行器,根据对应的sql直接执行即可,不会做一些额外的操作;
2、BatchExecutor执行器,顾名思义,通过批量操作来优化性能。通常需要注意的是批量更新操作,由于内部有缓存的实现,使用完成后记得调用flushStatements来清除缓存。
3、ReuseExecutor 可重用的执行器,重用的对象是Statement,也就是说该执行器会缓存同一个sql的Statement,省去Statement的重新创建,优化性能。内部的实现是通过一个HashMap来维护Statement对象的。由于当前Map只在该sqlsession中有效,所以使用完成后记得调用flushStatements来清除Map。

public class Configuration {
	//成员变量,通过读取配置文件进行初始化,是否开启二级缓存
	protected boolean cacheEnabled;
	//mapper中的每一条sql语句都会被封装成一个MappedStatement对象key = 全限定类名 + 方法名,value = 对应的MappedStatement对象
	protected final Map<String, MappedStatement> mappedStatements;
	//内部存放了mapper代理工厂对象,以及通过getMapper方法创建出代理对象并返回
	protected final MapperRegistry mapperRegistry;
	//这个对象主要封装了数据库连接池对象,事务对象,id
	protected Environment environment;
	//这个主要是连接数据库的信息,如user=root   password=1234  等实际上是一个hashTable
	protected Properties variables;

	public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
	        executorType = executorType == null ? this.defaultExecutorType : executorType;
	        executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
	        Object executor;
	        if (ExecutorType.BATCH == executorType) {
	            executor = new BatchExecutor(this, transaction);
	        } else if (ExecutorType.REUSE == executorType) {
	            executor = new ReuseExecutor(this, transaction);
	        } else {
	            executor = new SimpleExecutor(this, transaction);
	        }
	
	        if (this.cacheEnabled) {
	            executor = new CachingExecutor((Executor)executor);
	        }
	
	        Executor executor = (Executor)this.interceptorChain.pluginAll(executor);
	        return executor;
	    }
}

先来看看BaseExecutor的源码

public abstract class BaseExecutor implements Executor {
	//这个就是一级缓存,内部封装了一个hashMap  private Map<Object, Object> cache = new HashMap();而BaseExecutor又是sqlsession的一个属性所以说一级缓存的范围是sqlsession
    protected PerpetualCache localCache;
	//这个query的调用是通过sqlsession对象来调用的,当通过sqlsession调用方法时内部就调用了BaseExecutor的方法来执行sql语句
	public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
        ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
        if (this.closed) {
            throw new ExecutorException("Executor was closed.");
        } else {
            if (this.queryStack == 0 && ms.isFlushCacheRequired()) {
                this.clearLocalCache();
            }

            List list;
            try {
                ++this.queryStack;
                //先判断缓存中有没有
                list = resultHandler == null ? (List)this.localCache.getObject(key) : null;
                if (list != null) {
                	//注意这个key值是由CacheKey key = this.createCacheKey(ms, parameter, rowBounds, boundSql);这几个参数共同影响的
                    this.handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
                } else {
                	//这里是数据库中真正的查询。
                    list = this.queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
                }
            } finally {
                --this.queryStack;
            }

            if (this.queryStack == 0) {
                Iterator var8 = this.deferredLoads.iterator();

                while(var8.hasNext()) {
                    BaseExecutor.DeferredLoad deferredLoad = (BaseExecutor.DeferredLoad)var8.next();
                    deferredLoad.load();
                }

                this.deferredLoads.clear();
                if (this.configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
                    this.clearLocalCache();
                }
            }

            return list;
        }
    }
  
}

下面来看看DefaultSqlSession的源码

public class DefaultSqlSession implements SqlSession {
    private final Configuration configuration;
    private final Executor executor;
    private final boolean autoCommit;
	
	//从这看出通过Sqlsession执行sql语句实际上是通过executor.query来执行的。
	public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
        try {
            MappedStatement ms = this.configuration.getMappedStatement(statement);
            this.executor.query(ms, this.wrapCollection(parameter), rowBounds, handler);
        } catch (Exception var9) {
            throw ExceptionFactory.wrapException("Error querying database.  Cause: " + var9, var9);
        } finally {
            ErrorContext.instance().reset();
        }

    }  
}

关于二级缓存,注意二级缓存并不是只有一个,对于每一个MappedStatement有一个cache属性就是该sql语句对应的二级缓冲区。而每个MappedStatement在初始化Configuration时创建出来的。然后把cache作为key值通过获取value=TransactionalCache,TransactionalCache内部有个临时二级缓存,把cache传进去内部有方法flushPendingEntries将临时与cache之间进行数据赋值。

public class CachingExecutor implements Executor {
    private final Executor delegate;
    private final TransactionalCacheManager tcm = new TransactionalCacheManager();
	
	//这里传入的Executor是上文创建的三种Executor之一
    public CachingExecutor(Executor delegate) {
        this.delegate = delegate;
        delegate.setExecutorWrapper(this);
    }

    public Transaction getTransaction() {
        return this.delegate.getTransaction();
    }

    public void close(boolean forceRollback) {
        try {
            if (forceRollback) {
                this.tcm.rollback();
            } else {
                this.tcm.commit();
            }
        } finally {
            this.delegate.close(forceRollback);
        }

    }

    public boolean isClosed() {
        return this.delegate.isClosed();
    }

    public int update(MappedStatement ms, Object parameterObject) throws SQLException {
    	//注意这一点,如果再mybatis配置中配置了flushCache属性,那么再update时会清空临时缓冲区。否则只清理真正的二级缓冲区
        this.flushCacheIfRequired(ms);
        return this.delegate.update(ms, parameterObject);
    }

    public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
        BoundSql boundSql = ms.getBoundSql(parameterObject);
        CacheKey key = this.createCacheKey(ms, parameterObject, rowBounds, boundSql);
        return this.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
    }

    public <E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException {
        this.flushCacheIfRequired(ms);
        return this.delegate.queryCursor(ms, parameter, rowBounds);
    }

    public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    	//重点是这个真正存放二级缓存的地方,这个是每一个MappedStatement共用一个。所以二级缓存才能在不同sqlSession共享数据
        Cache cache = ms.getCache();
        if (cache != null) {
            this.flushCacheIfRequired(ms);
            if (ms.isUseCache() && resultHandler == null) {
                this.ensureNoOutParams(ms, boundSql);
                //先从二级缓存中取,如果map中不存在cache为key的value 那么就将cache作为参数创建TransactionalCache对象,并通过key从cache中获取数据。
                List<E> list = (List)this.tcm.getObject(cache, key);
                if (list == null) {
                	//这里是走一级缓存读取数据库的路子
                    list = this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
                    //这里是将读取的数据放进临时二级缓存只有执行commit之后才会将临时二级缓存中的数据放进MappedStatement中的cache中
                    this.tcm.putObject(cache, key, list);
                }

                return list;
            }
        }

        return this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
        
    }
    
	public void commit(boolean required) throws SQLException {
       	this.delegate.commit(required);
       	//实际上调用了TransactionalCache,中的commit方法
       	//这里的commit会将临时二级缓冲区中存进真正的二级缓冲区,并清空临时二级缓冲区,如果在调用commit之前调用了clear那么会先清理掉二级缓冲区再将临时缓冲区存进二级缓冲区
        this.tcm.commit();
    }
}

TransactionalCache类中的方法

	public void clear() {
        this.clearOnCommit = true;
        this.entriesToAddOnCommit.clear();
    }

    public void commit() {
        if (this.clearOnCommit) {
            this.delegate.clear();
        }

        this.flushPendingEntries();
        this.reset();
    }

    public void rollback() {
        this.unlockMissedEntries();
        this.reset();
    }

    private void reset() {
        this.clearOnCommit = false;
        this.entriesToAddOnCommit.clear();
        this.entriesMissedInCache.clear();
    }

    private void flushPendingEntries() {
        Iterator var1 = this.entriesToAddOnCommit.entrySet().iterator();

        while(var1.hasNext()) {
            Entry<Object, Object> entry = (Entry)var1.next();
            this.delegate.putObject(entry.getKey(), entry.getValue());
        }

        var1 = this.entriesMissedInCache.iterator();

        while(var1.hasNext()) {
            Object entry = var1.next();
            if (!this.entriesToAddOnCommit.containsKey(entry)) {
                this.delegate.putObject(entry, (Object)null);
            }
        }

    }

主要的过程就是这样了。我自己觉得思路写的不是很清晰,如果有错误请大家指出我会改正。写这个文章的目的主要是为了记笔记,同时也希望能够帮助别人。



这篇关于Mybatis源码解析的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程