> 文章列表 > Mapper接口的注册流程

Mapper接口的注册流程

Mapper接口的注册流程

文章目录

  • 介绍
  • 事例
  • 注册流程
    • MapperProxy类
    • MapperProxyFactory类
    • MapperRegistry类
  • 模拟手写一个代理流程
    • 1.创建一个Mapper接口
    • 2.代理接口
    • 3.创建代理类

介绍

Mapper接口用于定义执行SQL语句相关的方法,方法名一般和Mapper XML配置文件中<select|update|delete|insert>标签的id属性相同,接口的完全限定名一般对应Mapper XML配置文件的命名空间。


事例

在介绍Mapper接口之前,我们先来回顾一下如何执行Mapper中定义的方法,可参考下面的代码:

 @Testpublic  void testMybatis () throws IOException {// 获取配置文件输入流InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");// 通过SqlSessionFactoryBuilder的build()方法创建SqlSessionFactory实例SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);// 调用openSession()方法创建SqlSession实例SqlSession sqlSession = sqlSessionFactory.openSession();// 获取UserMapper代理对象UserMapper userMapper = sqlSession.getMapper(UserMapper.class);// 执行Mapper方法,获取执行结果List<UserEntity> userList = userMapper.listAllUser();System.out.println(JSON.toJSONString(userList));}

如上面的代码所示,在创建SqlSession实例后,需要调用SqlSession的getMapper()方法获取一个UserMapper的引用,然后通过该引用调用Mapper接口中定义的方法。

注册流程

MapperProxy类

我们知道,接口中定义的方法必须通过某个类实现该接口,然后创建该类的实例,才能通过实例调用方法。所以SqlSession对象的getMapper()方法返回的一定是某个类的实例。具体是哪个类的实例呢?实际上getMapper()方法返回的是一个动态代理对象。

MyBatis中通过MapperProxy类实现动态代理。下面是MapperProxy类的关键代码:

public class MapperProxy<T> implements InvocationHandler, Serializable {private static final long serialVersionUID = -6424540398559729838L;private final SqlSession sqlSession;private final Class<T> mapperInterface;private final Map<Method, MapperMethod> methodCache;public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {this.sqlSession = sqlSession;this.mapperInterface = mapperInterface;this.methodCache = methodCache;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {try {// 从Object类继承的方法不做处理if (Object.class.equals(method.getDeclaringClass())) {return method.invoke(this, args);} else if (isDefaultMethod(method)) {return invokeDefaultMethod(proxy, method, args);}} catch (Throwable t) {throw ExceptionUtil.unwrapThrowable(t);}// 对Mapper接口中定义的方法进行封装,生成MapperMethod对象final MapperMethod mapperMethod = cachedMapperMethod(method);// 执行方法return mapperMethod.execute(sqlSession, args);}
......
}

MapperProxy使用的是JDK内置的动态代理,实现了InvocationHandler接口,invoke()方法中为通用的拦截逻辑,具体内容在介绍Mapper方法调用过程时再做介绍。

MapperProxyFactory类

使用JDK内置动态代理,通过MapperProxy类实现InvocationHandler接口,定义方法执行拦截逻辑后,还需要调用java.lang.reflect.Proxy类的newProxyInstance()方法创建代理对象。

MyBatis对这一过程做了封装,使用MapperProxyFactory创建Mapper动态代理对象。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);}}

如上面的代码所示,MapperProxyFactory类的工厂方法newInstance()是非静态的。也就是说,使用MapperProxyFactory创建Mapper动态代理对象首先需要创建MapperProxyFactory实例。MapperProxyFactory实例是什么时候创建的呢?

MapperRegistry类

Configuration对象中有一个mapperRegistry属性,具体如下:

protected final MapperRegistry mapperRegistry = new MapperRegistry(this);

MyBatis通过mapperRegistry属性注册Mapper接口与MapperProxyFactory对象之间的对应关系。下面是MapperRegistry类的关键代码:

public class MapperRegistry {// Configuration对象引用private final Configuration config;// 用于注册Mapper接口Class对象,和MapperProxyFactory对象对应关系private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<Class<?>, MapperProxyFactory<?>>();public MapperRegistry(Configuration config) {this.config = config;}// 根据Mapper接口Class对象获取Mapper动态代理对象@SuppressWarnings("unchecked")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);}}public <T> boolean hasMapper(Class<T> type) {return knownMappers.containsKey(type);}// 根据Mapper接口Class对象,创建MapperProxyFactory对象,并注册到knownMappers属性中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 {knownMappers.put(type, new MapperProxyFactory<T>(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);}}}}......}

如上面的代码所示,MapperRegistry类有一个knownMappers属性,用于注册Mapper接口对应的Class对象和MapperProxyFactory对象之间的关系。另外,MapperRegistry提供了addMapper()方法,用于向knownMappers属性中注册Mapper接口信息。在addMapper()方法中,为每个Mapper接口对应的Class对象创建一个MapperProxyFactory对象,然后添加到knownMappers属性中。

MapperRegistry还提供了getMapper()方法,能够根据Mapper接口的Class对象获取对应的MapperProxyFactory对象,然后就可以使用MapperProxyFactory对象创建Mapper动态代理对象了。

MyBatis框架在应用启动时会解析所有的Mapper接口,然后调用MapperRegistry对象的addMapper()方法将Mapper接口信息和对应的MapperProxyFactory对象注册到MapperRegistry对象中。

模拟手写一个代理流程

1.创建一个Mapper接口

我们首先创建一个Mapper接口,用来模拟

public interface DevMapper {//表示获取设备信息DevPo getDev(int id);
}

2.代理接口

这个类可以看做MapperProxy,就是实现对接口的动态代理

public class MapperProxy implements InvocationHandler {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("参数为" + args);//这里看做Mybatis一顿解析最后成功查询出数据并返回DevPo devPo = new DevPo();devPo.setId(5);devPo.setName("zhixuchen");devPo.setNo("sadas");return devPo;}
}

3.创建代理类

这个类可以看做MapperProxyFactory,生成动态代理类

public class Test {public static void main(String[] args) throws ClassNotFoundException {DevMapper devMapper = (DevMapper) Proxy.newProxyInstance(DevMapper.class.getClassLoader(),new Class[]{Resources.classForName(DevMapper.class.getName())},new MapperProxy());DevPo dev = devMapper.getDev(5);System.out.println(dev.toString());}
}
输出结果:
参数为[Ljava.lang.Object;@20ad9418
DevPo{id=5, name='zhixuchen', no='sadas'}

学习记录,不断沉淀,终究会成为一个优秀的程序员,加油!
您的点赞、关注与收藏是我分享博客的最大赞赏!
博主博客地址: https://blog.csdn.net/qq_45701514