一、SqlSessionFactory
用于在连接或数据源中创建SqlSession,是一个单纯的创建SqlSession的工厂接口。
public interface SqlSessionFactory {SqlSession openSession();SqlSession openSession(boolean autoCommit);SqlSession openSession(Connection connection);SqlSession openSession(TransactionIsolationLevel level);SqlSession openSession(ExecutorType execType);SqlSession openSession(ExecutorType execType, boolean autoCommit);SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level);SqlSession openSession(ExecutorType execType, Connection connection);Configuration getConfiguration();}
二、SqlSession
The primary Java interface for working with MyBatis. Through this interface you can execute commands, get mappers and manage transactions.
如下图可见,SqlSession封装了我们所有的增删改查操作,是一个很重要的接口
三、mapper的生成
1、猜想的生成过程
自旋可以回忆:https://www.yuque.com/wangchao-volk4/fdw9ek/xmkynl
public class SupkingxLock {private static final Unsafe unsafe;private static final long valueOffset;private volatile int value = 0;static {try {Class<Unsafe> unsafeClass = Unsafe.class;Field theUnsafe = unsafeClass.getDeclaredField("theUnsafe");theUnsafe.setAccessible(true);unsafe = (Unsafe) theUnsafe.get(null);valueOffset = unsafe.objectFieldOffset(SupkingxLock.class.getDeclaredField("value"));} catch (Exception ex) {throw new Error(ex);}}public void lock() {// 自旋for (; ; ) {// this 当前对象本身,valueOffset 位移偏移量,0:内存中的真值,1:将要变成的值if (unsafe.compareAndSwapInt(this, valueOffset, 0, 1)) {return;}// 如果不是自己想要的值,则将当前线程让出去Thread.yield();}}public void unlock() {value = 0;}public static void main(String[] args) {System.out.println(unsafe);}}
public class MyBatisEnhance {public static final Map<Class<?>, Object> objectMap = new HashMap<>();private static SupkingxLock supkingxLock = new SupkingxLock();static class MapperScanner {private String mapperLocation;public List<Class<?>> scanMapper() {List<Class<?>> classList = new ArrayList<>();for (int i = 0; i < 500000000; i++) {classList.add(MyBatisEnhance.class);}return new ArrayList<>();}public String getMapperLocation() {return mapperLocation;}public void setMapperLocation(String mapperLocation) {this.mapperLocation = mapperLocation;}}/*** 可以优化来加个锁,实际Mybatis中并没有加锁* @param object*/public static void put(Object object) {supkingxLock.lock();objectMap.put(object.getClass(), object);supkingxLock.unlock();}public static void main(String[] args) {String mapperLocation = "classpath:mapper/*.xml";MapperScanner mapperScanner = new MapperScanner();mapperScanner.setMapperLocation(mapperLocation);/*** 一千个??*/List<Class<?>> classes = mapperScanner.scanMapper();// 解析xml// 。。。。。。。。long start = System.currentTimeMillis();// 将这1000个mapper添加到map中classes.forEach(aClass -> objectMap.put(aClass, new Object()));// classes.parallelStream().forEach(aClass -> put(new Object()));long end = System.currentTimeMillis();System.out.println(end - start);}}
2、MyBatis中mapper产生的实现方式
2.1 代码入口
public static void main(String[] args) throws IOException {InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();SqlSessionFactory factory = builder.build(in);SqlSession session = factory.openSession();//获得dao 的代理对象UserMapper userMapper = session.getMapper(UserMapper.class);User user = userMapper.queryById(21);System.out.println(user.toString());}
通过上述代码,很明显可以只知道,第一步载入 xml 文件,是从 SqlSessionFactoryBuilder 开始的
2.2 SqlSessioinFactoryBuilder
这个类负责解析 xml 并 build出 SqlSessionFactory
2.2.1 解析xml的入口
org.apache.ibatis.session.SqlSessionFactoryBuilder#build(java.io.InputStream, java.lang.String, java.util.Properties)
2.2.2 解析 xml 并将 mapper(interface)放入到configuration中

parse—>parseConfiguration—>mapperElement—>configuration.addMappers(mapperPackage);
根据配置文件中
如果是指定了标签mapper,则进入下图红框标出的位置,mapperParser.parse->bindMapperForNamespace
这个parse是用来解析写了sql的mapper.xml文件的
在 bindMapperForNamespace 方法中,通过书写了 sql 的 mapper.xml 文件中的 nameSpace 标签获取 mapper接口的全类名,然后通过反射获取到这个接口,最后将其 add 到 configuration 中
2.2.3 addMapper
addMapper方法就是将 接口mapper的位置作为key,value是一个封装了接口mapper的 MapperProxyFactory 对象。
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();public void addMappers(String packageName, Class<?> superType) {ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();resolverUtil.find(new ResolverUtil.IsA(superType), packageName);Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses();for (Class<?> mapperClass : mapperSet) {addMapper(mapperClass);}}public <T> void addMapper(Class<T> type) {if (type.isInterface()) {if (hasMapper(type)) {throw new BindingException("Type " + type + " is already known to the MapperRegistry.");}boolean loadCompleted = false;try {// 接口mapper的位置作为key,value是一个封装了接口mapper的 MapperProxyFactory 对象。knownMappers.put(type, new MapperProxyFactory<>(type));// It's important that the type is added before the parser is run// otherwise the binding may automatically be attempted by the// mapper parser. If the type is already known, it won't try.MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);parser.parse();loadCompleted = true;} finally {if (!loadCompleted) {knownMappers.remove(type);}}}}
2.2.4 MapperProxyFactory
public class MapperProxyFactory<T> {private final Class<T> mapperInterface;private final Map<Method, MapperMethodInvoker> methodCache = new ConcurrentHashMap<>();public MapperProxyFactory(Class<T> mapperInterface) {this.mapperInterface = mapperInterface;}public Class<T> getMapperInterface() {return mapperInterface;}public Map<Method, MapperMethodInvoker> 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<>(sqlSession, mapperInterface, methodCache);return newInstance(mapperProxy);}}
上述代码中的 MapperProxyFactory 中只是将 mapper 直接放到了 mapperInterface中,并没有区分这个mapper有注解的(@Select),还是用xml的。
这里接着 addMapper说
接着parser.parse()—>loadXmlResource()载入带sql的xml文件—->parseStatement(method),解析mapper接口上的注解方法,失败之后catch放入到configuration.addIncompleteMethod,然后再次parsePendingMethods执行失败的东西。
parse结束后,回到最上面 https://www.yuque.com/wangchao-volk4/fdw9ek/xpdqrv#lp5zo build返回 DefaultSqlSessionFactory 默认的。
3、是否可以优化 addMapper 方法
�这段代码并没用锁,也没有用ConcurrentHashMap。也没有使用 并行流(parallelStream().forEach(aClass -> put(new Object()) 的 这种添加方式。是不是可以优化一下???
什么时候应该用 parallelStream?可以参考现成的案例 java.util.Arrays#parallelSort(byte[])。
当数量小于 2的13次方或者并行数量是1的时候,用串行流,否则就要用并行流。
/*** The minimum array length below which a parallel sorting* algorithm will not further partition the sorting task. Using* smaller sizes typically results in memory contention across* tasks that makes parallel speedups unlikely.*/private static final int MIN_ARRAY_SORT_GRAN = 1 << 13;public static void parallelSort(byte[] a) {int n = a.length, p, g;if (n <= MIN_ARRAY_SORT_GRAN ||(p = ForkJoinPool.getCommonPoolParallelism()) == 1)DualPivotQuicksort.sort(a, 0, n - 1);elsenew ArraysParallelSortHelpers.FJByte.Sorter(null, a, new byte[n], 0, n, 0,((g = n / (p << 2)) <= MIN_ARRAY_SORT_GRAN) ?MIN_ARRAY_SORT_GRAN : g).invoke();}
四、获取mapper
public static void main(String[] args) throws IOException {InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();SqlSessionFactory factory = builder.build(in);SqlSession session = factory.openSession();//获得dao 的代理对象UserMapper userMapper = session.getMapper(UserMapper.class);User user = userMapper.queryById(21);System.out.println(user.toString());}
2.2.3 结束之后,我们获取到了一个 SqlSessionFactory,接口用这个工厂生产一个 SqlSession。
1、openSession

private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {Transaction tx = null;try {// 获得配置final Environment environment = configuration.getEnvironment();// 从配置中获得事务final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);// 创建事务tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);// 创建执行器final Executor executor = configuration.newExecutor(tx, execType);return new DefaultSqlSession(configuration, executor, autoCommit);} catch (Exception e) {closeTransaction(tx); // may have fetched a connection so lets call close()throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);} finally {ErrorContext.instance().reset();}}
2、getMapper
通过 创建的 SqlSession 中的 getMapper方法来获取mapper。其实就是获取 knownMappers 中的值(MapperProxyFactory)类型,接着,利用 MapperProxyFactory 中的 newInstance 方法创建一个代理对象(用的是jdk的proxy代理)。
knownMappers 中的值 是在 addMapper 时放入的
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);if (mapperProxyFactory == null) {throw new BindingException("Type " + type + " is not known to the MapperRegistry.");}try {return mapperProxyFactory.newInstance(sqlSession);} catch (Exception e) {throw new BindingException("Error getting mapper instance. Cause: " + e, e);}}
3、执行mapper
从MapperProxyFactory中获取到代理mapper后,就要开始执行。jdk的proxy的执行核心就是 InvocationHandler。请移步到下一节 3、MyBatis中关于mapper的执行过程
�
