Mybatis工作原理源码分析

 

Mybatis工作原理流程

1、全局配置文件解析

首先会解析全局配置文件mybatis-config.xml,下面我们来看看源码。现有下面的调用方式进行查询:

 

public void queryBlog() throws IOException {
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
​
        SqlSession session = sqlSessionFactory.openSession();
        try {
            BlogMapper mapper = session.getMapper(BlogMapper.class);
            BlogExample example = new BlogExample();
            List<Blog> list = mapper.selectByExample(example);
            System.out.println(list);
        } finally {
            session.close();
        }
}
我们跟进SqlSessionFactoryBuilderbuild()方法:
public SqlSessionFactory build(InputStream inputStream) {
        //调用类中重载的build方面
        return this.build((InputStream)inputStream, (String)null, (Properties)null);
}
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
        SqlSessionFactory var5;
        try {
            //XMLConfigBuilder它时负责解析全局配置文件的类,跟进XMLConfigBuilder类中
            XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
            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 XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
        this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
}
//发现会new Configuration()对象
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
    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;
}

看到new Configuration()对象时,可以猜到这个就是全局配置文件映射类。

我们回到SqlSessionFactoryBuilderbuild()方法。

var5 = this.build(parser.parse());

可以看到parse()就是具体的解析配置文件的方法,继续跟进进入XMLConfigBuilder

 

解析这部分我们就不细看了,就以解析mappers为例:可以看到会将mapper对象放入Configuration的mapperRegistry属性中。

Class<?> mapperInterface = Resources.classForName(mapperClass);
this.configuration.addMapper(mapperInterface);
//再跟进看看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 {
                //knownMappers为HashMap,key为mapper接口类型,value为接口的工厂类。
                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);
                }
​
            }
        }
​
    }

通过对节点的解析,然后将属性赋值给Configuration对象中的属性。解析完成后,调用build()方法。

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

DefaultSqlSessionFactory是SqlSessionFactory接口的默认实现。下面通过时序图更直观的来看看:

 

2、创建会话

SqlSession session = sqlSessionFactory.openSession();

点进跟进查看openSession()的实现,可以在DefaultSqlSessionFactory类中看到如下方法:

private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
        Transaction tx = null;
​
        DefaultSqlSession var8;
        try {
            Environment environment = this.configuration.getEnvironment();
            TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment);
            tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
            //执行器,Exector是直接操作数据库的接口类,
            Executor executor = this.configuration.newExecutor(tx, execType);
            //返回会话DefaultSqlSession
            var8 = new DefaultSqlSession(this.configuration, executor, autoCommit);
        } catch (Exception var12) {
            this.closeTransaction(tx);
            throw ExceptionFactory.wrapException("Error opening session.  Cause: " + var12, var12);
        } finally {
            ErrorContext.instance().reset();
        }
​
        return var8;
    }


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);
        }
        //this.interceptorChain是插件拦截器链
        Executor executor = (Executor)this.interceptorChain.pluginAll(executor);
        return executor;
    }

Executor类关系图如下:BaseExecutor为抽象类

创建会话时序图如下:

 

3、获取代理对象

会话创建完成后,接下来我们继续看看如何通过getMapper()获得mapper接口的。

BlogMapper mapper = session.getMapper(BlogMapper.class);

点进跟进,在DefaultSqlSession类中。

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

然后从Configuration配置对象的mapperRegistry属性中取出,在前面第一步解析配置文件是,将mapper接口放入了HashMap中。

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

继续跟进源码,在MapperProxyFactory类中发现了,看到如下代码,就豁然开朗了,这是JDK动态代理的实现啊,也就是最后返回了代理对象。

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

接着我们看看MapperProxy类,最后会带哦用invoke()方法。

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
            if (Object.class.equals(method.getDeclaringClass())) {
                return method.invoke(this, args);
            }
​
            if (this.isDefaultMethod(method)) {
                return this.invokeDefaultMethod(proxy, method, args);
            }
        } catch (Throwable var5) {
            throw ExceptionUtil.unwrapThrowable(var5);
        }
​
        MapperMethod mapperMethod = this.cachedMapperMethod(method);
        return mapperMethod.execute(this.sqlSession, args);
}

看看这部分的时序图:

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值