首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >MyBatis是如何让我们通过接口就能调用到SQL的

MyBatis是如何让我们通过接口就能调用到SQL的

原创
作者头像
code-x
修改2022-08-17 14:41:53
4810
修改2022-08-17 14:41:53
举报
文章被收录于专栏:code-xcode-xcode-x

大致可分为如下几个步骤

1. 动态注册bean

1.1 根据配置mapperScan, 扫描对应的包, 将对应的类解析成BeanDefinition
1.2 通过替换BeanDefinition中的BeanClass为MapperFactoryBean, (原来的BeanClass是Mapper接口) 实现了在spring生成对应的对象时, 返回的对象不是本身类型的对象,而是MapperFactoryBean重写FactoryBean接口的getObject()方法返回的代理对象。该方法getObject()已经对mapper接口进行了代理, 即后续进行自动注入时, 也是返回getObject()生成的代理对象

2. 生成对应的代理对象

2.1 在getObject()方法中, 会获取到接口的全限定名称, 然后进一步对代理方法进行封装, 调用链如下
 	   MapperFactoryBean: 
       	public T getObject() throws Exception {
       		return getSqlSession().getMapper(this.mapperInterface);
       	}
       
       DefaultSqlSession: 
           public <T> T getMapper(Class<T> type) {
           	//configuration是mybatis的重要配置类, 在初始化的时候, 就会将mapper接口添加到configuration中
           	return configuration.getMapper(type, this);
           }
       
       Configuration: 
       	public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
       		return mapperRegistry.getMapper(type, sqlSession);
       	}
       
       MapperRegistry: 
       	public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
       	    //获取mapper代理工厂
       		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: 
       	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);
       	}
2.2 在org.apache.ibatis.binding.MapperProxy#cachedInvoker中, new PlainMethodInvoker(new MapperMethod(mapperInterface, method, sqlSession.getConfiguration())), 并且将代理方法调用器缓存起来. MapperMethod该对象即是最终调用方法的对象.

3. 执行对应的方法

3.1 方法调用, 代理里最常见的方法invoke
      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
        	//过滤掉object的方法
          if (Object.class.equals(method.getDeclaringClass())) {
            return method.invoke(this, args);
          } else {
            return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
          }
        } catch (Throwable t) {
          throw ExceptionUtil.unwrapThrowable(t);
        }
      }
3.2. org.apache.ibatis.binding.MapperProxy.PlainMethodInvoker重写的invoke方法中,判断该方法是否被调用过(是否存在于缓存), 若没有, 则创建一个PlainMethodInvoker方法调用器, 传入MapperMethod(MapperMethod是真正执行方法的对象), 并将新创建的PlainMethodInvoker存入缓存中(methodCache), 并调用该PlainMethodInvoker的invoke方法,
3.3. xml中的id和select等标签封装成了SqlCommand, 调用mapperMethod的execute, 执行对应的增删改查.

4. 结果集封装, 进行一些数据库数据对应java对象的转换

通过mybatis的封装和代理, 将mapper.xml转换成了接口的实例对象

如有谬误, 欢迎斧正

简化版如下: https://blog.csdn.net/sinat_25991865/article/details/89891581

public interface UserMapper {
	List<SysUser> selectAll();
}
public class MyMapperProxy<T> implements InvocationHandler {
	private Class<T> mapperInterface;
	private SqlSession sqlSession;
	
	public MyMapperProxy(Class<T> mapperInterface, SqlSession sqlSession) {
			this.mapperInterface = mapperInterface;
			this.sqlSession = sqlSession;
	}

	@Override
	public Object invoke(Object proxy , Method method , Object[] args)
			throws Throwable {
		//针对不同的 sql 类型,需要调用sqlSession不同的方法
		//接口方法中的参数也有很多情况 ,这里只考虑没有有参数的情况
		List<T> list= sqlSession.selectList(
				mapperInterface.getCanonicalName() + ”.” + method.getName());
		//返回数据也有很多情况,这里不做处理直接返回
		return list;
	}
}

方法调用

//获取sqlSession
SqlSession sqlSession = getSqlSession();
//获取 UserMapper 接口
MyMapperProxy userMapperProxy = new MyMapperProxy(
		UserMapper.class , sqlSession) ;
UserMapper userMapper = (UserMapper) Proxy.newProxyinstance (
		Thread.currentThread().getContextClassLoader(),
		new Class[ ] {UserMapper.class},
		userMapperProxy) ;
//调 用 selectAll 方 法
List<SysUser> user= userMapper.selectAll();

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 动态注册bean
    • 1.1 根据配置mapperScan, 扫描对应的包, 将对应的类解析成BeanDefinition
      • 1.2 通过替换BeanDefinition中的BeanClass为MapperFactoryBean, (原来的BeanClass是Mapper接口) 实现了在spring生成对应的对象时, 返回的对象不是本身类型的对象,而是MapperFactoryBean重写FactoryBean接口的getObject()方法返回的代理对象。该方法getObject()已经对mapper接口进行了代理, 即后续进行自动注入时, 也是返回getObject()生成的代理对象
      • 2. 生成对应的代理对象
        • 2.1 在getObject()方法中, 会获取到接口的全限定名称, 然后进一步对代理方法进行封装, 调用链如下
          • 2.2 在org.apache.ibatis.binding.MapperProxy#cachedInvoker中, new PlainMethodInvoker(new MapperMethod(mapperInterface, method, sqlSession.getConfiguration())), 并且将代理方法调用器缓存起来. MapperMethod该对象即是最终调用方法的对象.
          • 3. 执行对应的方法
            • 3.1 方法调用, 代理里最常见的方法invoke
              • 3.2. org.apache.ibatis.binding.MapperProxy.PlainMethodInvoker重写的invoke方法中,判断该方法是否被调用过(是否存在于缓存), 若没有, 则创建一个PlainMethodInvoker方法调用器, 传入MapperMethod(MapperMethod是真正执行方法的对象), 并将新创建的PlainMethodInvoker存入缓存中(methodCache), 并调用该PlainMethodInvoker的invoke方法,
                • 3.3. xml中的id和select等标签封装成了SqlCommand, 调用mapperMethod的execute, 执行对应的增删改查.
                • 4. 结果集封装, 进行一些数据库数据对应java对象的转换
                  • 通过mybatis的封装和代理, 将mapper.xml转换成了接口的实例对象
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档