接着上一篇MyBatis动态代理(一),这一篇我们来追踪下MyBatis的动态代理
0. 通过SqlSessionFactoryUtil获取SqlSession
package com.pengjs.kkb.mybatis.utils;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.InputStream;
/**
* @author: Jason
* @date: 2020-06-21 21:13
*/
public class SqlSessionFactoryUtil {
/**
* SqlSessionFactory对象
*/
private static SqlSessionFactory sqlSessionFactory = null;
/**
* 类线程锁
*/
private static final Class CLASS_LOCK = SqlSessionFactoryUtil.class;
/**
* 私有化构造参数,不让实例化,只能通过类来访问
* 避免使用者使用new的方式去创建多个对象
*/
private SqlSessionFactoryUtil() {}
/**
* 构建SqlSessionFactory,单利模式,懒汉式
* 某一个对象在应用中承担唯一职责的时候就是用单利模式,在本例中,SqlSessionFactory的唯一职责就是创建SqlSession
*/
private static void initSqlSessionFactory() {
String resource = "mybatis_config.xml";
InputStream inputStream = null;
try {
inputStream = Resources.getResourceAsStream(resource);
} catch (Exception e) {
e.printStackTrace();
}
// 静态方法使用类锁
synchronized (CLASS_LOCK) {
if (sqlSessionFactory == null) {
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}
}
}
/**
* 打开SqlSession
* @return SqlSession
*/
public static SqlSession openSqlSession() {
if (sqlSessionFactory == null) {
initSqlSessionFactory();
}
// 获取数据库信息,对应mybatis配置文件中的databaseIdProvider,现在我们用的是MySQL,对应的value就是mysql,即databaseId
String databaseId = sqlSessionFactory.getConfiguration().getDatabaseId();
System.out.println("databaseId: " + databaseId);
return sqlSessionFactory.openSession();
}
}

1. 入口,通过SqlSession获取Mapper接口


2. SqlSession默认实现类DefaultSqlSession

3. 从全局的Configuration中获取已经装载的mapper

4. 从mapperProxyFactory中获取mapper


5. 我们来看看MapperProxyFactory
是不是就是MyBatis动态代理(一)讲到的JDK动态代理了。
/**
* Copyright 2009-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.binding;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.ibatis.session.SqlSession;
/**
* @author Lasse Voss
*/
public class MapperProxyFactory<T> {
private final Class<T> mapperInterface;
private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>();
public MapperProxyFactory(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
public Class<T> getMapperInterface() {
return mapperInterface;
}
public Map<Method, MapperMethod> getMethodCache() {
return methodCache;
}
@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
}
newInstance(mapperProxy)生成的就是一个动态代理类:

下面我们来看看代理类的invoke方法
MapperMethod采用命令模式运行,根据上下文跳转,它可能跳转到许多方法中,我们不需要全部明白。我们可以看到里面的executeForMany方法,再看看它的实现,实际上它最后就是通过sqlSession对象去运行对象的SQL。






至此,相信大家已经了解了MyBatis为什么只用Mappper接口便能够运行SQL,因为映射器的XML文件的命名空间对应的便是这个接口的全路径,那么它根据全路径和方法名便能够绑定起来,通过动态代理技术,让这个接口跑起来。而后采用命令模式,最后还是使用SqlSession接口的方法使得它能够执行查询,有了这层封装我们便可以使用接口编程,这样编程就更简单了。
网友评论