范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

Mybatis动态代理解析

  1、引言
  Mybatis 作为一种持久层框架,它支持自定义 SQL、存储过程以及高级映射。里面大量使用了代理技术,这篇文章将从以下几个问题对 Mybatis 中的代理进行解析。
  Mybatis Mapper 接口在无实现类的情况下,如何实现的动态代理。
  JDK 动态代理为什么不能对非接口类进行代理。 2、JDK 动态代理2.1、基于接口的动态代理
  •代码实例 public interface StudentDao {     public void sayHello(); }  public class StudentDaoImpl implements StudentDao{     @Override     public void sayHello() {         System.out.println("你好");     } }  public class ProxyHandler implements InvocationHandler {     private Object target;      public ProxyHandler(Object target) {         this.target = target;     }      @Override     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {         System.out.println("调用代理类");         return method.invoke(target, args);     }      public static void main(String[] args) {         StudentDao studentDao = new StudentDaoImpl();         StudentDao proxy = (StudentDao) Proxy.newProxyInstance(studentDao.getClass().getClassLoader(), studentDao.getClass().getInterfaces(), new ProxyHandler(studentDao));         proxy.sayHello();      } }
  上述代码运行后,可以发现对应 proxy 对象有一个特殊标记(Proxy拼接一个数字),这代表该对象的类型是一个代理对象。那么这个代理类是如何生成的,这些标记是如何构建的,下面我们走入 Proxy.newProxyInstance 里面去一瞅真相。
  • newProxyInstance
  该方法主要是生成代理类,其内部主要的流程为 getProxyClass0 与 newInstance ,其时序图如下所示:
  1.getProxyClass0 会创建代理类
  2.newInstance 会绑定代理类和增强类的关系
  3、Mybatis 中的动态代理
  Mybatis 未出现之前,开发人员需要在每个 mapper 层的实现类中重复进行如下操作,对数据库进行增删查改。 // 装载Mysql驱动  Class.forName(driveName);  // 获取连接  con = DriverManager.getConnection(url, user, pass);  // 创建Statement  Statement state = con.createStatement();  // 构建SQL语句  String stuQuerySqlStr = "SELECT * FROM STUDENT";  // 执行SQL返回结果  ResultSet result = state.executeQuery(stuQuerySqlStr);
  Mybatis 针对该痛点,利用 JDK 动态代理对以上逻辑进行封装,使用者只需要自定义 Mapper 和 xml 文件,利用注解或者 xml 的形式定义 SQL 语句,项目启动时通过解析器解析 SQL 语句组装为 Java 中的对象。
  但是日常使用 Mybatis 过程中我们都是直接定义的 mapper 接口,没有定义其实现类,那么 JDK 动态代理是如何代理未实现的接口呐,和上文具有实现类的接口代理有什么区别?
  •未实现类接口的动态代理 public interface Subject {      String sayHello();  }    public class ProxyInvocationHandler implements InvocationHandler {        @Override      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {          System.out.println("进入代理调用处理器 ");          return "success";      }  }
  可以发现对于未实现类接口的动态代理,其 InvocationHandler 并未执行代理类的方法,而是执行完增强方法后就返回InvocationHandler 的返回值;而具有实现类接口的动态代理则会执行代理类方法并且返回其执行结果。
  那么,Mybatis 对于 maaper 接口的代理方法是怎样的呐,其 InvocationHandler 的具体实现细节是什么?我们接着分析。
  •MapperProxyFactory
  该类是通过工厂模式去生成 mapper 代理类,其中 mapperInterface 是等待生成代理对象的接口。 public class MapperProxyFactory {    private final Class mapperInterface;   private final Map methodCache = new ConcurrentHashMap<>();    public MapperProxyFactory(Class mapperInterface) {     this.mapperInterface = mapperInterface;   }    public Class getMapperInterface() {     return mapperInterface;   }    public Map getMethodCache() {     return methodCache;   }    @SuppressWarnings("unchecked")   protected T newInstance(MapperProxy mapperProxy) {     return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);   }    public T newInstance(SqlSession sqlSession) {     final MapperProxy mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);     return newInstance(mapperProxy);   }  }
  这里我们重点关注该类中的两个 newInstance 方法,由于 mapper 是一个接口,最终代理类的调用是执行的 InvocationHandler 中的 invoke 方法,所以最终的 mybatis 封装 mapper 的操作放在 MapperProxy 中。我们接着看里面的细节。
  •MapperProxy
  由于该类代码较多,我们这里只关注封装部分的方法逻辑,该部分逻辑获取对应 mapper 中的方法的增强实现,并调用其 invoke 方法。 MapperMethod类是整个代理机制的核心类,对SqlSession中的操作进行了封装使用。 该类里有两个内部类SqlCommand和MethodSignature。 SqlCommand用来封装CRUD操作,也就是我们在xml中配置的操作的节点。每个节点都会生成一个MappedStatement类。MethodSignature用来封装方法的参数以及返回类型   private MapperMethodInvoker cachedInvoker(Method method) throws Throwable {     try {       // A workaround for https://bugs.openjdk.java.net/browse/JDK-8161372       // It should be removed once the fix is backported to Java 8 or       // MyBatis drops Java 8 support. See gh-1929       MapperMethodInvoker invoker = methodCache.get(method);       if (invoker != null) {         return invoker;       }        return methodCache.computeIfAbsent(method, m -> {         if (m.isDefault()) {           try {             if (privateLookupInMethod == null) {               return new DefaultMethodInvoker(getMethodHandleJava8(method));             } else {               return new DefaultMethodInvoker(getMethodHandleJava9(method));             }           } catch (IllegalAccessException | InstantiationException | InvocationTargetException               | NoSuchMethodException e) {             throw new RuntimeException(e);           }         } else {           return new PlainMethodInvoker(new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));         }       });     } catch (RuntimeException re) {       Throwable cause = re.getCause();       throw cause == null ? re : cause;     }   }
  •MapperMethod
  该类是整个代理机制的核心类,对SqlSession中的操作进行了封装使用。 从而实现了 CRUD,其最后还是回到 SqlSession 中进行调用数据库。 ublic Object execute(SqlSession sqlSession, Object[] args) {     Object result;     switch (command.getType()) {       case INSERT: {         Object param = method.convertArgsToSqlCommandParam(args);         result = rowCountResult(sqlSession.insert(command.getName(), param));         break;       }       case UPDATE: {         Object param = method.convertArgsToSqlCommandParam(args);         result = rowCountResult(sqlSession.update(command.getName(), param));         break;       }       case DELETE: {         Object param = method.convertArgsToSqlCommandParam(args);         result = rowCountResult(sqlSession.delete(command.getName(), param));         break;       }       case SELECT:         if (method.returnsVoid() && method.hasResultHandler()) {           executeWithResultHandler(sqlSession, args);           result = null;         } else if (method.returnsMany()) {           result = executeForMany(sqlSession, args);         } else if (method.returnsMap()) {           result = executeForMap(sqlSession, args);         } else if (method.returnsCursor()) {           result = executeForCursor(sqlSession, args);         } else {           Object param = method.convertArgsToSqlCommandParam(args);           result = sqlSession.selectOne(command.getName(), param);           if (method.returnsOptional()               && (result == null || !method.getReturnType().equals(result.getClass()))) {             result = Optional.ofNullable(result);           }         }         break;       case FLUSH:         result = sqlSession.flushStatements();         break;       default:         throw new BindingException("Unknown execution method for: " + command.getName());     }     if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {       throw new BindingException("Mapper method "" + command.getName()           + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");     }     return result;   }

7款顶级旗舰续航横评祝贺你,iPhone13ProMax!表现一骑绝尘续航对每一位用户来讲都是非常重要的体验,手机没有电什么也干不了,而且很多人有电量焦虑症,见不得手机电量蹭蹭地往下掉,那么目前旗舰机中哪款续航最好呢?本文会给你一个答案。统计测试7款早餐别出去买了,教你简单做家常饼,记住2个小技巧,好吃不硬大家好,今天分享一款,柔软多层,简单快速的家常饼,真正的凉了都不硬,吃过我烙的这个饼呀,早餐再也不想出去买了,我家每周烙几次,大人和孩子都爱吃和面比例面粉300克盐3克酵母0。5克3道常吃的家常菜,简单易做,营养美味,家有孩子一定要学会今天推荐3道常吃的家常菜,简单易做,营养美味,家里有孩子的一定要学会!一肉丝炒西兰花制作配料西兰花1朵鸡胸肉1块生抽2勺老抽1勺蚝油1勺盐1勺淀粉1勺大蒜2个制作方法1鸡胸肉切成丝华为MatePadPaper上手体验HarmonyOS2成为最大看点在上个月底的MWC2022大会上,华为正式发布了MatePadPaper,这也是华为首款墨水平板产品,随着其2999元的国内售价公布,笔者也在第一时间拿到了这款产品。其实从今年的市揭秘悟空号卫星太空延寿,离不开地上的大管家截至2021年12月17日,悟空号卫星已在轨飞行了整整6年。作为中国科学院空间科学先导专项一期发射的首颗卫星,悟空的设计寿命本来只有3年,但直到现在,6岁的悟空仍然保持着旺盛而充沛为啥在内蒙古大草原上,当地人更爱煮羊肉,而不是美味的烤羊肉?只要真正在草原生活过的人就能明白其中缘由,烤羊肉虽然吃上去更美味,但真正的草原人却很少这样吃。以前我一直认为是人们觉得烤羊肉太麻烦,但一位在草原生活的朋友告诉我草原的真实环境比我们冰雪运动增添旅游活力来源人民网人民日报辽宁省阜新市黄家沟村滑雪场,游客在雪上飞驰。本报记者刘洪超摄滑冰场边,静静停放蒸汽机车接待大厅,高高悬起大型起重机滑雪道两侧,座座炼铁高炉耸立在辽宁省阜新市细河区蒙古国南戈壁省,狼群泛滥,而牧民的日常工作是打狼和对骆驼弹琴蒙古国是一个很传奇的国度,广阔的草原一望无际。但也不都是草原,还有戈壁。蒙古国南戈壁比较出名的省份是南戈壁省,面积16。5万平方公里,是全国省区面积最大的省,下辖有14个行政县,5K宝瓦里耶娃俄罗斯花样滑冰人气之王,生于喀山的冰上精灵在国际花样滑冰界,如果说最内卷的国家,非俄罗斯莫属。在俄罗斯国内有着各种各样的娃,她们个个外貌出众,且实力不菲。正是因为国内如此浓厚的花样滑冰传统,以及运动员们之间激烈的竞争环境,一人一车自驾游云南70多天,每天住酒店,消费清单参考一下吧云南是个好地方,那里山奇水美,风景秀丽。南边的西双版纳属亚热带气候,只有炎热的夏季,没有严寒,堪比海南三亚。北边的玉龙雪山,在北纬30度上,千年积雪不化,比日本的富士山还美。苍山,安徽开在荒野的农家菜馆,十几口柴锅并排操作,人气爆棚日卖3万城市里的美食固然多,但有一些意想不到的美食往往出现在一些农村里,它藏匿在一些名不见经传的小村庄,今天介绍的这一家饭店就开在荒郊野岭的村庄里,可谓是麻雀虽小武藏俱全,到底是什么样的美
3015!3017!全联盟都不看好他们,感谢老詹,他终于重拾巅峰了NBA联盟现在有两支球队非常难受,一支是东部篮网队,惨遭7连败,夺冠前景渺茫,欧文不打疫苗的事情无法解决,杜兰特在养伤,哈登也拉伤腿筋,而且篮网对交易哈登持开放态度。另一支是西部湖德甲2莱比锡多赛一场9分领跑北京时间2月6日凌晨1点30分,20212022赛季德甲联赛第21轮迎来一场焦点战,领头羊拜仁慕尼黑坐镇主场迎战莱比锡。上半场,穆勒进球打破僵局,安德烈席尔瓦破门扳平比分,莱万的进霸气放言我的眼睛就是尺,王濛凭啥?昨晚,中国短道速滑混合接力队拿下本届冬奥会中国代表团首金,全网沸腾!王濛因为在和黄健翔搭档解说本场比赛时妙语频出,一连上了7个热搜,宝藏奥运冠军段子手藏不住了!这是王濛的解说首秀,一夜9大交易动态!湖人向多队兜售5名球员,火箭欢迎哈登回归北京时间2月6日,距离本赛季的交易截止日又近一天,过去这一夜,NBA又有哪些新的交易动态?湖人正在兜售5名球员,火箭欢迎哈登回归休斯顿,76人和9队讨论西蒙斯交易,多支球队有意追求辽宁找到郭士强的接班人!他是最强的高中生,天赋胜过郭艾伦问如何每天都能收到如此有趣的体育原创资讯?答只需轻点右上角的关注按钮就能实现梦想。说起CBA球队的青训,球迷第一时间想到会是广东队,青年队主教练曲绍斌培养了一大批年轻球员,而且朱芳见世面了!荷兰冠军吃麻辣烫,加拿大冠军首次见蹲坑厕所很惊讶目前北京冬奥会已经开幕,盛大的开幕式非常精彩,继北京夏季奥运会开幕式之后,再次震撼了全世界,许多看过日本2021年日本奥运会开闭幕式的观众都表示,差距太大了。实际上,我们历来非常重创造中国短道速滑历史,王濛跪地感谢,李琰教练的传奇一生她是我国首位站上冬奥领奖台的女子短道速滑运动员,是我国短道速滑的教母级人物,带出了无数个世界冠军,并多次带领他们出征冬奥会。王濛周洋武大靖韩天宇任子威都是她的弟子,王濛更是称她改变把饺子皮扔进滚烫的油锅中,瞬间变美食,我家一周吃6次,真解馋大家好,我是真真,秋冬季节,给大家分享一个饺子皮特别好吃的做法。把饺子皮放到油锅里面炸一炸,像我这样做出来,大人小孩都特别的喜欢吃。需要准备的食材饺子皮红枣葡萄干黑芝麻白芝麻这是我北京风景,你知道前门大栅栏吗,这里有条以美食而闻名的胡同位于北京前门大街的大栅栏,想必很多人都不会陌生,尤其是在这春节小长假期间,大栅栏更是人头攒动,不知道来此的小伙伴们,有没有留意,在大栅栏的周边,有许多有趣的胡同,而其中的门框胡同更美国奥运名将展示奥运村餐厅美食什么都有,十分美味冬奥村餐厅的食物(视频截图)海外网2月4日电近日,美国单板滑雪传奇运动员肖恩怀特抵达北京,开启他的第五次冬奥之旅。在比赛前夕,这位奥运名将在社交媒体上分享了冬奥村的餐厅,不仅称什么你知道月球是怎么出现的吗?带你了解神秘月球的冷知识在夜空之中最亮的就是月亮了,虽然它有阴晴圆缺,但是它依然是最亮的,我们对月球也有着许多美丽的神话和传说,不过其实对于月亮我们却知之甚少。虽然每天都能看到它,但是它始终遮着一块神秘的