站在巨人的肩膀上,感谢!

mybatis源码分析之Mapper代理实现分析

2017年11月21日 23:39:04 huangshanchun 阅读数:277
 
 版权声明:欢迎转载,如有不足之处,恳请斧正。如果有错误,欢迎指出。 https://blog.csdn.net/huangshanchun/article/details/78597789

0 概述

使用过mybatis框架的人都知道,我们只是写了一个个mapper接口但是没有写它的实现类,但是我们可以直接使用它调用其对应的接口执行相应的sql语句。其实很容易想到它是使用代理来实现的,那么究竟是怎么实现的呢?本文主要来揭开这一神秘面纱。

1 代理模式

之前写过一篇代理模式的简介,一般使用代理方式如下,有个Target接口以及其实现类,Proxy中会调用具体Target实现类。具体实例可见:代理模式 

2 Mapper接口代理实现的源码分析

我们知道Mapper接口是没有具体的实现类,那么是怎么个代理法?其实是一种约定,但是正是因为这种约定可以大大的简化开发。约定Mapper接口XML文件一一对应,这样在invoke方法中就可以通过相应的类名、方法名获取到相应的xml文件配置的sql语句,从而执行对应的sql语句。

如下面实例,不难发现mapper类名和xml 中namespace对应上,mapper中方法名和对应的id对应上。

  1. package com.hsc.dao;
  2. import com.hsc.entity.Book;
  3. /**
  4. * Created by hsc on 17/7/22.
  5. */
  6. public interface BookMapper {
  7. int insert(Book book);
  8. }

对应xml文件

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  3. <mapper namespace="com.hsc.dao.BookMapper">
  4. <sql id="Base_Column_List">
  5. `id`,`name`
  6. </sql>
  7. <insert id="insert" useGeneratedKeys="true" keyProperty="id"
  8. parameterType="com.hsc.entity.Book">
  9. insert into book(name)
  10. values(
  11. #{name})
  12. </insert>
  13. </mapper>

MapperProxyFactory 工厂类

  1. /**
  2. * @author Lasse Voss
  3. */
  4. public class MapperProxyFactory<T> {
  5. //Mapper接口
  6. private final Class<T> mapperInterface;
  7. private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>();
  8. public MapperProxyFactory(Class<T> mapperInterface) {
  9. this.mapperInterface = mapperInterface;
  10. }
  11. public Class<T> getMapperInterface() {
  12. return mapperInterface;
  13. }
  14. public Map<Method, MapperMethod> getMethodCache() {
  15. return methodCache;
  16. }
  17. //返回接口的代理对象,基于JDK的原生的代理方式
  18. @SuppressWarnings("unchecked")
  19. protected T newInstance(MapperProxy<T> mapperProxy) {
  20. return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  21. }
  22. public T newInstance(SqlSession sqlSession) {
  23. final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
  24. return newInstance(mapperProxy);
  25. }
  26. }

MapperProxy 是实现InvocationHandler接口,重点关注下invoke方法的实现

  1. public class MapperProxy<T> implements InvocationHandler, Serializable {
  2. private static final long serialVersionUID = -6424540398559729838L;
  3. private final SqlSession sqlSession;
  4. //mapper 接口
  5. private final Class<T> mapperInterface;
  6. private final Map<Method, MapperMethod> methodCache;
  7. public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
  8. this.sqlSession = sqlSession;
  9. this.mapperInterface = mapperInterface;
  10. this.methodCache = methodCache;
  11. }
  12. @Override
  13. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  14. try {
  15. // 这里是为了做兼容,因为MapperProxyFactory生成的代理对象其祖先也是Object,比如代理对象调用toString方法就会默认调用Object类中的
  16. if (Object.class.equals(method.getDeclaringClass())) {
  17. return method.invoke(this, args);
  18. } else if (isDefaultMethod(method)) {
  19. return invokeDefaultMethod(proxy, method, args);
  20. }
  21. } catch (Throwable t) {
  22. throw ExceptionUtil.unwrapThrowable(t);
  23. }
  24. //根据配置找到对应执行方法(进行包装)
  25. final MapperMethod mapperMethod = cachedMapperMethod(method);
  26. //执行
  27. return mapperMethod.execute(sqlSession, args);
  28. }
  29. private MapperMethod cachedMapperMethod(Method method) {
  30. MapperMethod mapperMethod = methodCache.get(method);
  31. if (mapperMethod == null) {
  32. mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
  33. methodCache.put(method, mapperMethod);
  34. }
  35. return mapperMethod;
  36. }
  37. @UsesJava7
  38. private Object invokeDefaultMethod(Object proxy, Method method, Object[] args)
  39. throws Throwable {
  40. final Constructor<MethodHandles.Lookup> constructor = MethodHandles.Lookup.class
  41. .getDeclaredConstructor(Class.class, int.class);
  42. if (!constructor.isAccessible()) {
  43. constructor.setAccessible(true);
  44. }
  45. final Class<?> declaringClass = method.getDeclaringClass();
  46. return constructor
  47. .newInstance(declaringClass,
  48. MethodHandles.Lookup.PRIVATE | MethodHandles.Lookup.PROTECTED
  49. | MethodHandles.Lookup.PACKAGE | MethodHandles.Lookup.PUBLIC)
  50. .unreflectSpecial(method, declaringClass).bindTo(proxy).invokeWithArguments(args);
  51. }
  52. /**
  53. * Backport of java.lang.reflect.Method#isDefault()
  54. */
  55. private boolean isDefaultMethod(Method method) {
  56. return ((method.getModifiers()
  57. & (Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC)) == Modifier.PUBLIC)
  58. && method.getDeclaringClass().isInterface();
  59. }
  60. }

3 小结

mybatis通过这种约定以及使用代理模式这种巧妙的设计方式是值得我们思考和学习的。

Mybatis思的更多相关文章

  1. 使用mybatis访问sql server

    原创文章转载请注明出处:@协思, http://zeeman.cnblogs.com mybatis作为一种半自动化的ORM工具,可以提供更大的灵活性,逐渐受到社区的欢迎. 官方下载地址是:https ...

  2. myBatis实例

    一.搭建环境, 建立数据库 CREATE TABLE user( id ) not NULL AUTO_INCREMENT, userName ) DEFAULT NULL, userAge ) DE ...

  3. JavaEE开发之SpringBoot整合MyBatis以及Thymeleaf模板引擎

    上篇博客我们聊了<JavaEE开发之SpringBoot工程的创建.运行与配置>,从上篇博客的内容我们不难看出SpringBoot的便捷.本篇博客我们继续在上篇博客的基础上来看一下Spri ...

  4. MyBatis源码解读(3)——MapperMethod

    在前面两篇的MyBatis源码解读中,我们一路跟踪到了MapperProxy,知道了尽管是使用了动态代理技术使得我们能直接使用接口方法.为巩固加深动态代理,我们不妨再来回忆一遍何为动态代理. 我相信在 ...

  5. 关于Mybatis的一次pingQuery时间间隔的实践及思考

    转眼间离这次问题的实践过程已经过去了一两个月了,现在想来自己的问题并不是不知道那么简单了,所以很有必要记录下来,算是一次警戒吧 废话不多说,直入主题. 我的直接上级准备将公司的后台管理系统由PHP转为 ...

  6. SpringBoot+MyBatis配置多数据源

    SpringBoot 可以支持多数据源,这是一个非常值得学习的功能,但是从现在主流的微服务的架构模式中,每个应用都具有唯一且准确的功能,多数据源的需求很难用到,考虑到实际情况远远比理论复杂的多,这里还 ...

  7. spring+mybatis+mina+logback框架搭建

    第一次接触spring,之前从来没有学过spring,所以算是赶鸭子上架,花了差不多一个星期来搭建,中间遇到各种各样的问题,一度觉得这个框架搭建非常麻烦,没有一点技术含量,纯粹就是配置,很低级!但随着 ...

  8. SpringBoot整合MyBatis及Thymeleaf

    http://www.cnblogs.com/ludashi/archive/2017/05/08/6669133.html 上篇博客我们聊了<JavaEE开发之SpringBoot工程的创建. ...

  9. Spring 整合Mybatis dao原始方法

    先看一下项目图,基本就理解了整合的内容 这次主角不再是Mybats的配置文件SqlMapConfig.xml了,而是Spring的applicationContext.xml applicationC ...

随机推荐

  1. 【LeetCode】Construct Binary Tree from Preorder and Inorder Traversal

    Given preorder and inorder traversal of a tree, construct the binary tree. Note:You may assume that ...

  2. SQL 系统表应用

    查看link server select DD.System,DD.DB,DD.previous_processing_dte, DD.processing_dte,LS.LinkServerName ...

  3. PySpider安装与使用(Windows系统下)

    PySpider Begin 安装pip install pyspider 在windows系统好像会出现如下问题 Command "python setup.py egg_info&quo ...

  4. const与define应用上该怎么取舍

    const与define应用上该怎么取舍 #define WYB 100; const float WYB = 100; define是在预编译的时候展开替换的,const是编译运行阶段使用 defi ...

  5. [usaco2008feb_gold]路面修整

      FJ打算好好修一下农场中某条凹凸不平的土路.按奶牛们的要求,修好后的路面高度应当单调上升或单调下降,也就是说,高度上升与高度下降的路段不能同时出现在修好的路中. 整条路被分成了N段,N个整数A_1 ...

  6. CentOS(Linux) - 安装软件笔记(总) - 开发环境安装顺序及汇总

    1.安装java环境 参考文章 CentOS7.1 使用资源搜集 2.需要可视化管理服务器时,需要先安装VPSmate 参考文章 CentOS(Linux) - 安装软件笔记(一) - VPSMate ...

  7. JavaWeb项目里面的路径获取方法总结

    仅为资源搬运,个人还未充分理解... request.getRealPath不推荐使用request.getRealPath("") 这个方法已经不推荐使用了 request.ge ...

  8. eclipse订制快捷键

    步骤: 1.window-preference. 2.在(1)处输入keys,在(2)处输入命令的原来的快捷键,方便找到Binding,在(3)处输入自定义的快捷键.点击“apply and clos ...

  9. Yii的缓存机制之动态缓存

    当整个页面被缓存,但只有小部分区域需要根据不同的条件设置不同的信息.(例如商品的详细页面的缓存中用户名是动态的)这里就需要设置动态缓存. 首先在被缓存的模板中使用renderDynamic进行动态渲染 ...

  10. Swift类型转换

    关于「类型转换」(Type Casting),<The Swift Programming Language>描述如下: Type casting is a way to check th ...