谈谈 MyBatis 中 Mapper 接口方法的执行过程
你是否也曾好奇MyBatis中的Mapper当中的方法是如何被执行?这篇文章通过源码解析其中的流程。
概述
MyBatis通过JDK动态代理的方式创建Mapper接口的代理对象,当Mapper代理对象调用Mapper接口中定义的方法时,会调用InvocationHandler中的invoke()方法,执行相应的拦截逻辑,从而完成数据库操作。
执行过程
我们知道Mapper由两部分组成:Mapper接口和通过注解或者XML文件配置的SQL语句。以下通过一个单元测试的代码来看如何执行Mapper中定义的方法:
//加载主配置文件 Reader reader = Resources.getResourceAsReader("mybatis-config.xml") //通过主配置文件信息构建 sqlSessionFactor,在这里会加载Mapper的配置信息:调用MapperRegistry类中的addMapper方法将信息放入缓存中 SqlSessionFactory sqlMapper = new SqlSessionFactoryBuilder().build(reader); //获取SqlSession实例 SqlSession session = sqlMapper.openSession() //通过动态代理获取Mapper代理对象 AuthorMapper mapper = session.getMapper(AuthorMapper.class); //执行Mapper中的方法:会被代理类中的invoke方法拦截 List<Author> authors = mapper.selectAllAuthors();
跟踪源码,可以看到完整的流程:
1、DefaultSqlSession.java: SqlSession接口的默认实现类 //SqlSession中定义了获取Mapper对象的方法,DefaultSqlSession中实现了这个方法:实际上是调用Configuration类中getMapper方法 public <T> T getMapper(Class<T> type) { return configuration.getMapper(type, this); } 2、Configuration.java: 用于封装配置文件信息的类 //Mapper注册类实例 protected final MapperRegistry mapperRegistry = new MapperRegistry(this); public <T> T getMapper(Class<T> type, SqlSession sqlSession) { return mapperRegistry.getMapper(type, sqlSession); } 3、MapperRegistry.java:Mapper信息注册和获取 private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>(); 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); } } 4、MapperProxyFactory.java:Mapper代理工厂类 //通过 Proxy.newProxyInstance 生成代理对象 public T newInstance(SqlSession sqlSession) { final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache); return newInstance(mapperProxy); } protected T newInstance(MapperProxy<T> mapperProxy) { //通过Proxy类获取代理对象实例 return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy); }
总结一下:MyBatis框架在应用启动时会解析所有的Mapper接口,然后调用MapperRegistry对象的addMapper()方法将Mapper接口信息和对应的MapperProxyFactory(Mapper代理工厂类)对象注册到knownMappers缓存中。所以可以直接从缓存中获取Mapper接口类型对应MapperProxyFactory对象,从而构造出MapperProxy实例。
通用的执行逻辑
在MapperProxy类中定义了通用的执行逻辑,当调用Mapper当中的方法时便会执行这些逻辑:MapperProxy中的invoke方法:
@Override 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 { //注:cachedInvoker(method).invoke会执行 mapperMethod.execute(sqlSession, args)方法,完成对数据库的操作 return cachedInvoker(method).invoke(proxy, method, args, sqlSession); } } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } }
个人博客:https://www.kangpeiqin.cn/#/index