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

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;   }

生于五十年代的你们身边存下多少养老金了?会怎么处理这笔钱?我是56年的,老伴55年的,我一月退休金整3000,老伴1300,家有70多平米住房一处,沒有车,和别人比什么都不行,但有一样可和别人比,就是健康,现我们两人都很健康,争取多活几年企业身份在退休前转为事业编制有没有意义?企业退休前转为事业编制确实有作用。如果在老人老办法这种转换很难,但是一旦转换成功,退休待遇会存5000元,直接上升到1万元退休。养老保险并轨自从2014年开始之后,中人10年过渡期人到中年的标志是什么?我妈妈是71年的,今年50周岁,虽然希望妈妈永远年轻,但是她还是悄悄地步入中年了。1越来越不喜欢热闹,越来越享受肃静。越来越不喜欢过年,过年感觉身心疲惫。不喜欢走亲戚,不喜欢热闹,你见过的混得最惨的中年人到底有多惨,为什么没有人帮他们?村里的一个发小,最近因为天气冷。有天傍晚,找了个梯子踩上,往高处的窗户上钉塑料布时,因为梯子没人扶,向后滑倒了。他从梯子上摔了下来,立马就不能动了。由于他居住的老宅在旧村里,邻近的用一句话来证明你是武汉人,你会说什么?感谢邀请,因为我自己不是土生土长武汉人。但在武汉也生活了17年了,很多人觉得要会说武汉话就证明自己是武汉人,那我现在我也会说。2009年当时跟同事看武汉说唱团的那个年度大戏一碗都是马上又到正月十五元宵节,元宵节为什么吃元宵,点蜡烛,猜灯谜?正月十五元宵节也被称为上元节,是丰富多彩的正月里的节日,自古流传有吃元宵,点蜡烛,猜灯谜等节日讲究,节日文化,中华文化,博大精深,源远流长,流传千古,逐渐形成了一种节日文化。正月十在越来越高的房价之下,为什么人们反而热衷于买房?房价越来越高,说明房子越来越值钱啊,所以才有那么多人热衷买房!如果房价越来越低,房子越来越不值钱,谁还把钱投在房产上?人们热衷于买房,至少说明两点。一,房价上涨的可能性大于下跌的可60岁老人应不应该享受免费的旅游景点门票?我认为国家对六十岁的老年人旅游景点门票免费。是对老年人的关爱和呵护。我们大力支持和举双手赞成!有些年青人对老年人六十岁享受旅游景点免费存在看法和异意。这也不足为奇,因为他们没有生长50岁病退和60岁正常退休的每月退休金差多少?这个差多少恐怕谁也无法给你十分精确的答案,包括社保部门的人员。因为它要受多种因素的影响。但我可以自身为例说明我是52岁那年病退的,目前拿到4100元我妹婿是60岁退的,他现在是43在西安一个月工资4800是不是很低很低?西安到手3500是堵墙,大概超过一半的人月到手工资也就是3500左右,甚至更低。个人感觉西安工资3500左右非常多,往上5000又是一档。市场就这样,工资高的也就那些行业。我一个月我是刚上大一的女生,每个月向父母要4000块钱的生活费算多吗?谢谢邀请!我冒昧的问一下,你上的是清华还是北大?从你所说的看出,你是刚上大学的新生,你为什么要向父母要这么多钱,在你的心中4000元是什么样的概念,才使你很轻松的脱口而出4000元
专访最高检四厅厅长张晓津积极推动涉案企业合规改革走深走实南财集团全国两会报道组记者王俊杨希实习生张玲北京报道近年来金融犯罪专业性更加突出,涉互联网金融犯罪高发多发,上下游分工负责各司其职的现象更加明显,金融犯罪黑灰产业链危害越来越大。今全国人大代表丁波保持忧患意识保证企业不断发展来源中国经济网马瀚明姜智文只有多听基层的声音,才能传递基层的声音。全国人大代表,上汽集团乘用车郑州分公司党委书记总经理丁波在接受中国经济网记者采访时说道。全国人大代表,上汽集团乘用2023亚运会限定手举牌丨文化宣传打call必备道具喜迎接2023亚运会,我们一起为亚运会打call今年在杭州即将举行亚运会,我们也是很早就开始上亚运会系列手举牌上面的文案都适用于做宣传活动,或者手举打call在做活动时可以将这些粘湖南省融资担保集团部署靠企吃企问题专项整治工作会议现场。红网时刻新闻3月10日讯(通讯员唐利艳)3月8日上午,湖南省融资担保集团有限公司召开靠企吃企问题专项整治动员部署暨警示教育会议,学习贯彻省纪委三次全会和省管国有企业靠企吃巧用GenericObjectPool创建自定义对象池作者京东物流高圆庆1前言通常一个对象创建销毁非常耗时的时候,我们不会频繁的创建和销毁它,而是考虑复用。复用对象的一种做法就是对象池,将创建好的对象放入池中维护起来,下次再用的时候直湖南衡阳移动总经理更替原邵阳移动总经理周友庚接任运营商财经吴碧慧文据天眼查登记的工商资料显示,衡阳移动负责人在今年年初左右进行了更替,现任公司总经理由原邵阳移动总经理周友庚接任,运营商财经网试图介绍他的在职背景。据了解,周友庚早苏州首次开评数字经济工程职称,3个专业可报来自苏州人社部门的消息,苏州市首次数字经济工程职称评审近日已启动,包括集成电路工业互联网区块链三个专业,申报人员可在规定时间内进行网上申报。苏州人社部门持续深化人才评价改革,率先在2023她力量丨商汤绝影智能汽车事业群商务副总裁杨琳人和人的差别远远大于性别的差别每经记者可杨每经编辑陈俊杰力量曾与传统意义中的柔美相冲撞,但社会意识的进步正消弭刻板印象,当越来越多人谈论起女性主义,期望在对话之中若隐若现或有一天,性别不再是桎梏。这场漫长的沟通英美陆续制裁华为,终于迎来恶果,华为开始全面反击!有这么一家来自中国的民营企业,不仅仅是惨遭美国的制裁,更是陆续被多个国家制裁,而这个企业就是华为,很多人只以为是美国在打压华为,实际上为了遏制华为的发展,美国老早就拉拢多国盟友一起新疆木垒县打造千万千瓦级风光新能源基地3月8日,在木垒县新疆东方风电新能源有限公司车间外,一台正在准备运走的风力发电机组。陶拴科摄中新网新疆木垒3月9日电(陶拴科)三月,万物复苏,在新疆东天山木垒县四十个井子风区,茫茫六问行业库存现状趋势影响问题1本轮库存周期的整体情况如何?2000年以来,我国经历了6轮完整的库存周期,当前处于第7轮库存周期的主动去库阶段。截至2022年12月,工业企业产成品存货累计同比9。9,处于2