Mybatis源码解读

        MyBatis是我们工作中常见的ORM持久层框架,对于MyBatis并不能仅仅局限于会使用的阶段,更需要了解它的工作原理,想要了解原理,源码是必须要读的,这篇文章是我个人在阅读MyBatis的源码过程中的一些简单的总结,MyBatis的流程大体上可以分为一下几步:加载配置文件 --> 创建SqlSessionFactory --> 创建SqlSession --> 执行SQL语句 --> 返回结果。步骤代码如下:

public static void main(String[] args) {  
    String resource = "mybatis-config.xml";  
    SqlSession sqlSession = null;  
    // 1、读取mybatis-config.xml  
    try (InputStream inputStream = Resources.getResourceAsStream(resource)) {  
        // 2、解析mybatis-config.xml配置文件,通过SqlSessionFactoryBuilder.build创建sqlSessionFactory  
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);  
        // 3、创建sqlSession  
        sqlSession = sqlSessionFactory.openSession();  
        Map<String, Object> paramsMap = new HashMap<>(2);  
        paramsMap.put("id", 1);  
        paramsMap.put("name", "jams");  
        //4、执行sql  
        List<User> list = sqlSession.selectList("com.c.mybatis.mapper.UserMapper.selectByName", paramsMap);  
        log.info("响应结果:{}", JSONUtil.toJsonStr(list));  
    } catch (Exception e) {  
        e.printStackTrace();  
    } finally {  
        assert sqlSession != null;  
        sqlSession.close();  
    }  
}

这段代码是一个整体的过程展示,下面对这些步骤进行逐步拆解。

1、加载配置信息

        加载配置信息的目的是获取数据源信息和Mapper.xml等文件配置信息。
源码如下:

String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);

这部分是一个文件读取信息,将配置信息读取为InputStream的过程。

2、创建SqlSessionFactory

        SqlSessionFactory是MyBatis中一个重要的接口,SqlSessionFactory的创建是通过SqlSessionFactoryBuilder的build方法中进行,其源码如下:

SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

其中构建的详细逻辑在build方法中,build方法的源码如下:

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
        SqlSessionFactory var5;
    try {
        //创建XMLConfigBuilder对象
        XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
        //创建SqlSessionFactory对象
        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;
}

上面的源码中有两处是需要继续阅读的,一个是new XMLConfigBuilder,一个是pase()方法,new XMLConfigBuilder将读取的文件流转成XMLConfigBuilder对象并进行了一系列的初始化操作,其中最重要的是进行了Configuration的创建,Configuration是一个极为重要的上下文对象。我们对这两个步骤的源码进行继续跟进。new XMLConfigBuilder的源码如下:
 

private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
        //创建Configuration对象
        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;
}

其中super(new Configuration());操作进行了各类的属性初始话,源码如下:

public Configuration() {
    this.safeResultHandlerEnabled = true;
    this.multipleResultSetsEnabled = true;
    this.useColumnLabel = true;
    this.cacheEnabled = true;
    this.useActualParamName = true;
    this.localCacheScope = LocalCacheScope.SESSION;
    this.jdbcTypeForNull = JdbcType.OTHER;
    this.lazyLoadTriggerMethods = new HashSet(Arrays.asList("equals", "clone", "hashCode", "toString"));
    this.defaultExecutorType = ExecutorType.SIMPLE;
    this.autoMappingBehavior = AutoMappingBehavior.PARTIAL;
    this.autoMappingUnknownColumnBehavior = AutoMappingUnknownColumnBehavior.NONE;
    this.variables = new Properties();
    this.reflectorFactory = new DefaultReflectorFactory();
    this.objectFactory = new DefaultObjectFactory();
    this.objectWrapperFactory = new DefaultObjectWrapperFactory();
    this.lazyLoadingEnabled = false;
    this.proxyFactory = new JavassistProxyFactory();
    this.mapperRegistry = new MapperRegistry(this);
    this.interceptorChain = new InterceptorChain();
    this.typeHandlerRegistry = new TypeHandlerRegistry();
    this.typeAliasRegistry = new TypeAliasRegistry();
    this.languageRegistry = new LanguageDriverRegistry();
    this.mappedStatements = (new Configuration.StrictMap("Mapped Statements collection")).conflictMessageProducer((savedValue, targetValue) -> {
        return ". please check " + savedValue.getResource() + " and " + targetValue.getResource();
    });
    this.caches = new Configuration.StrictMap("Caches collection");
    this.resultMaps = new Configuration.StrictMap("Result Maps collection");
    this.parameterMaps = new Configuration.StrictMap("Parameter Maps collection");
    this.keyGenerators = new Configuration.StrictMap("Key Generators collection");
    this.loadedResources = new HashSet();
    this.sqlFragments = new Configuration.StrictMap("XML fragments parsed from previous mappers");
    this.incompleteStatements = new LinkedList();
    this.incompleteCacheRefs = new LinkedList();
    this.incompleteResultMaps = new LinkedList();
    this.incompleteMethods = new LinkedList();
    this.cacheRefMap = new HashMap();
    this.typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
    this.typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);
    this.typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
    this.typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
    this.typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);
    this.typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
    this.typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
    this.typeAliasRegistry.registerAlias("LRU", LruCache.class);
    this.typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
    this.typeAliasRegistry.registerAlias("WEAK", WeakCache.class);
    this.typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);
    this.typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class);
    this.typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class);
    this.typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
    this.typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);
    this.typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
    this.typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
    this.typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
    this.typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
    this.typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);
    this.typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class);
    this.typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class);
    this.languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
    this.languageRegistry.register(RawLanguageDriver.class);
}

到这里,做的最重要的操作是创建了一个Configuration对象,XMLConfigBuilder对象创建完成后,调用XMLConfigBuilder的pase()方法,该方法的主要作用是解析配置文件,并对配置文件中的各个节点进行处理,例如生成数据源对象DataSource,将Mapper.xml文件和持久层接口映射,保存Mapper.xml中的各个sql语句,以便后续执行sql时,获取对应的sql。其源码如下:

public Configuration parse() {
    //parsed用来标识是否已经在解析中,parsed = true表示已经有线程在启动了,无需再次加载信息,抛出异常
    if (this.parsed) {
        throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    } else {
        //标识正在加载 
        this.parsed = true;
        //进行配置文件信息的解析,处理配置文件中的各个节点
        this.parseConfiguration(this.parser.evalNode("/configuration"));
        return this.configuration;
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值