⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 https://my.oschina.net/u/657390/blog/748646 「udbwcso」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注**微信公众号:【芋道源码】**有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言将得到认真回复。甚至不知道如何读源码也可以请教噢
  4. 新的源码解析文章实时收到通知。每周更新一篇左右
  5. 认真的源码交流微信群。

上一篇:mybatis源码分析之SqlSession的创建过程

https://my.oschina.net/u/657390/blog/663991

重点分析了SqlSession的创建过程.SqlSession创建成功后:

String resource = "com/analyze/mybatis/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
Map map = sqlSession.selectOne("com.analyze.mybatis.mapper.UserMapper.getUA");
sqlSession.close();

String resource = "com/analyze/mybatis/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
Map map = userMapper.getUA();
sqlSession.close();

以上两段代码最终将得到相同的结果.

对比可以发现两段代码不同之处为:

Map map = sqlSession.selectOne("com.analyze.mybatis.mapper.UserMapper.getUA");

UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
Map map = userMapper.getUA();

根据sqlSession.selectOne("com.analyze.mybatis.mapper.UserMapper.getUA");

可以想像selectOne会用com.analyze.mybatis.mapper.UserMapper.getUA查找相应的配置,然后执行sql.

接着分析sqlSession.selectOne()

DefaultSqlSession.java
public <T> T selectOne(String statement) {
return this.<T>selectOne(statement, null);
}

public <T> T selectOne(String statement, Object parameter) {
// Popular vote was to return null on 0 results and throw exception on too many.
List<T> list = this.<T>selectList(statement, parameter);
if (list.size() == 1) {
return list.get(0);
} else if (list.size() > 1) {
throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
} else {
return null;
}
}

public <E> List<E> selectList(String statement, Object parameter) {
return this.selectList(statement, parameter, RowBounds.DEFAULT);
}


public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
//从配置中获取statement信息
MappedStatement ms = configuration.getMappedStatement(statement);
//调用执行器
return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}

但UserMapper userMapper = sqlSession.getMapper(UserMapper.class); Map map = userMapper.getUA();这两行代码能够看出一些问题,在代码里并没有实现UserMapper这个接口,也就是说userMapper.getUA();并没实现,最终却能够被调用.那么这到底是怎么实现的呢?

进一步分析源码:

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

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

MapperRegistry.java
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);
}
}

从以上代码可以看出最终调用的是mapperProxyFactory.newInstance()

接着分析MapperProxyFactory的源码

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很容易就可以看出使用了动态代理.

再来看看MapperProxy里的invoke

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//当执行的方法是继承自Object时执行this里的相应方法
if (Object.class.equals(method.getDeclaringClass())) {
try {
return method.invoke(this, args);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
final MapperMethod mapperMethod = cachedMapperMethod(method);
//最终执行的是execute方法
return mapperMethod.execute(sqlSession, args);
}

从以上代码可以看出调用mapper里的接口的时候执行的都是mapperMethod.execute(sqlSession, args);

继续跟进代码会发现上面的例子最终执行的是

sqlSession.selectOne(command.getName(), param);

可谓殊途同归.

mapper动态代理涉及到的类有MapperRegistry,MapperProxyFactory,MapperProxy,MapperMethod

img

MapperRegistry的数据源头Configuration.java

MapperRegistry mapperRegistry = new MapperRegistry(this);

XMLConfigBuilder中的方法parseConfiguration()调用mapperElement(root.evalNode("mappers"));

mapperElement()会调用addMapper()最后将数据添加到MapperRegistry中的knownMappers

分析完源码可以仿照以上代码来实现自己的功能:

public interface UserService {
Map getUser();
}


public class ServiceProxy implements InvocationHandler {

public <T> T newInstance(Class<T> clz) {
return (T) Proxy.newProxyInstance(clz.getClassLoader(), new Class[]{clz}, this);
}

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
}
System.out.println("----proxy----" + method.getName());
return null;
}
}

//测试代码
public static void main(String[] args) {
UserService userService = new ServiceProxy().newInstance(UserService.class);
userService.toString();
userService.getUser();
}

//输出结果
----proxy----getUser

666. 彩蛋

如果你对 MyBatis 感兴趣,欢迎加入我的知识一起交流。

知识星球

文章目录
  1. 1. 666. 彩蛋