Mybaits如何做到调用接口?

毫无疑问,动态代理,本文将透过一些源码深化代理模式

1.源码篇

1)生成代理

先上测试代码,相关包扫描和Mybaits的配置文件都已经写好了

public class MapperTest {@Testpublic void testMapper() throws IOException, MyException {ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:/spring/applicationContext-dao.xml");SqlSessionFactory factory = (SqlSessionFactory) context.getBean("sqlSessionFactory");//可DEBUG这一行,这里创建并返回了Mapper的代理TestMapper mapper = factory.openSession().getMapper(TbItemMapper.class);//发送一个查询请求,这一行揭示了如何做到调用接口mapper.selectByExample(new TestMapper ());}
}

这里不必细看,知道在哪进入即可

public class MapperRegistry {public <T> T getMapper(Class<T> type, SqlSession sqlSession) {//检索Mapper是否注册代理工厂MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);if (mapperProxyFactory == null) {throw new BindingException("Type " + type + " is not known to the MapperRegistry.");} else {try {//进入这一行,就可以看到代理的产生return mapperProxyFactory.newInstance(sqlSession);} catch (Exception var5) {throw new BindingException("Error getting mapper instance. Cause: " + var5, var5);}}}//此处省略其他代码
}

注意重载方法,该对象已经装配好了配置文件中的接口信息,还配置了一个缓存

  • MapperProxy就是一个InvocationHandler
  • 再用他生成真正的JDK代理对象并返回
    至此动态代理对象已经生成
public class MapperProxyFactory<T> {private final Class<T> mapperInterface;private Map<Method, MapperMethod> methodCache = new ConcurrentHashMap();protected T newInstance(MapperProxy<T> mapperProxy) {//JDK代理***return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);***}//DEBUG进入在这一行public T newInstance(SqlSession sqlSession) {MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);return this.newInstance(mapperProxy);}//此处省略其他代码
}

2)方法如何调用

invoke方法接收到方法调用,只做了两件事

  • 创建并缓存MapperMethod类
  • 调用MapperMethod.execute()方法
//实现了InvocationHandler
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;}public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//如果调用了Object类的方法if (Object.class.equals(method.getDeclaringClass())) {try {return method.invoke(this, args);} catch (Throwable var5) {throw ExceptionUtil.unwrapThrowable(var5);}} else {//缓存并创建一个MapperMethod对象,KEY method VALUE mapperMethodMapperMethod mapperMethod = this.cachedMapperMethod(method);//把session和方法参数委托给MapperMethodreturn mapperMethod.execute(this.sqlSession, args);}}private MapperMethod cachedMapperMethod(Method method) {MapperMethod mapperMethod = (MapperMethod)this.methodCache.get(method);if (mapperMethod == null) {//用接口信息,方法信息,配置信息构造MapperMethod mapperMethod = new MapperMethod(this.mapperInterface, method, this.sqlSession.getConfiguration());this.methodCache.put(method, mapperMethod);}return mapperMethod;}
}

MapperMethod 类做了什么?(指令模式?)

  • 将方法调用信息包装成SqlCommand
  • 将方法xml配置信息包装成MethodSignature
  • 发送SQL语句,获取结果返回
public class MapperMethod {private final MapperMethod.SqlCommand command;private final MapperMethod.MethodSignature method;public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {this.command = new MapperMethod.SqlCommand(config, mapperInterface, method);this.method = new MapperMethod.MethodSignature(config, method);}//进入这个方法,匹配指令执行SQLpublic Object execute(SqlSession sqlSession, Object[] args) {Object param;Object result;if (SqlCommandType.INSERT == this.command.getType()) {param = this.method.convertArgsToSqlCommandParam(args);result = this.rowCountResult(sqlSession.insert(this.command.getName(), param));} else if (SqlCommandType.UPDATE == this.command.getType()) {param = this.method.convertArgsToSqlCommandParam(args);result = this.rowCountResult(sqlSession.update(this.command.getName(), param));} else if (SqlCommandType.DELETE == this.command.getType()) {param = this.method.convertArgsToSqlCommandParam(args);result = this.rowCountResult(sqlSession.delete(this.command.getName(), param));} else {if (SqlCommandType.SELECT != this.command.getType()) {throw new BindingException("Unknown execution method for: " + this.command.getName());}if (this.method.returnsVoid() && this.method.hasResultHandler()) {this.executeWithResultHandler(sqlSession, args);result = null;} else if (this.method.returnsMany()) {result = this.executeForMany(sqlSession, args);} else if (this.method.returnsMap()) {result = this.executeForMap(sqlSession, args);} else {param = this.method.convertArgsToSqlCommandParam(args);result = sqlSession.selectOne(this.command.getName(), param);}}if (result == null && this.method.getReturnType().isPrimitive() && !this.method.returnsVoid()) {throw new BindingException("Mapper method '" + this.command.getName() + " attempted to return null from a method with a primitive return type (" + this.method.getReturnType() + ").");} else {return result;}}
}


自然而然,这个类之后的事就和我们使用JDBC一样,拼装SQL,调用SQL,装配返回结果
但这里的动态代理和以往我所见过的不同,以往InvocationHandler需要持有代理对象的引用,在这里却没有,因为Mapper本身并不存在实现,就是这一点引发了我的思考


2.深化代理模式

代理是什么?我的思想转变(不包括CGLIB)

阶段一:初识AOP
这个问题在我初学AOP的时候遇到,当时我的理解跟随着AOP,生成代理的目的就是给方法进行增强,在方法前后添加业务逻辑,接口,所以代理就是一种方法增强的模式,而事务很适合用这一点。毕竟就是在方法前后添加开启事务,提交事务。
而Proxy.newProxyInstance(classloader,interface,invocationhandler)这个API,拿到手我是看不懂的。

阶段二:印象强化
在网上看了一些文章,理解到了静态代理,才感知到动态代理,是Java运用反射机制,沿用代理的思想的一个实现,目的就是为了解耦。
Proxy.newProxyInstance的参数就很明显了

  • classloader和interface 运行时反射创建代理类
  • invocationhandler 持有代理对象的引用,负责转发的请求处理
    当然我以为这样就结束了

阶段三:代理模式
走马观花看了一遍《headfirst设计模式》,才知道代理的玩法太多了,代理更像是一个影武者(傀儡),方法增强不过是冰山一角,比如保护代理,在代理类中编写如果调用了XXX方法就抛出异常等等。

阶段四:开拓视野
我看的源码肯定不多,所以Mybaits中的代理着实让我开了眼界,对于没有实现类的接口也可以代理,传递方法和参数,委托自己的类来进行处理,方法信息和SQL语句绑定,调用方法就会执行相关的Statement,原来SQL语句是这么调用的,当然过程比我说的复杂得多。


3.总结

1.Mybaits中的Mapper接口使用动态代理来实现方法调用
2.Mapper的动态代理并不处理方法,而是转发给MapperMethod。
3.Mapper接口不需要实现类,但需要有相关的XML配置,因为MapperMethod根据方法匹配XML配置

转载于:https://www.cnblogs.com/haon/p/11353434.html

深化代理模式,Mybaits如何做到调用接口相关推荐

  1. Java 设计模式_代理模式(2016-08-19)

    概念: 代理模式是对象的结构模式.代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用. 就是一个人或者机构代表另一个人或者机构采取行动.在一些情况下,一个客户不想或者不能够直接引用一 ...

  2. Python设计模式-代理模式

    Python设计模式-代理模式 基于Python3.5.2,代码如下 #coding:utf-8info_struct = dict() info_struct["addr"] = ...

  3. java反射学习(2):反射与代理模式

    一 基本代理设计模式 代理模式核心思路,一个接口有两个子类,一个子类完成业务需求,另一个完成辅助功能 假设实现一个功能,张三吃饭 代码如下: 接口 PersonDao.java package com ...

  4. JAVA设计模式 - 代理模式

    在对象的一个业务方法完成之后, 有时候我们可能需要去添加一些新的功能(前置校验等). 但我们又不想更改原来的代码 , 代理模式就为我们提供了一种解决方案 . 1 . 代理模式的定义 代理模式就是在不改 ...

  5. 第 15 章 代理模式

    第 15 章 代理模式 1.代理模式的基本介绍 代理模式:为一个对象提供一个替身,以控制对这个对象的访问. 即通过代理对象访问目标对象. 代理对象的好处是:可以在目标对象实现的基础上,增强额外的功能操 ...

  6. 设计模式之禅【代理模式】

    真刀实枪之代理模式 我是游戏至尊 "最近几年王者荣耀的热度飙升,自己打时可以体验到其中的升级乐趣,但是时间过得很快啊!自己不想打,找代练,好主意!" 作为一名程序员,先将打游戏这段 ...

  7. Java的三种代理模式【附源码分析】

    Java的三种代理模式&完整源码分析 代理模式分为两种,静态代理和动态代理,动态代理包括JDK动态代理和Cglib动态代理. 静态代理 静态代理在使用时,需要定义接口或者父类,被代理对象与代理 ...

  8. 如何理解java代理模式?

    需求 假设现在有一个需求:在项目现有所有类的方法前后打印日志. 你如何在不修改已有代码的前提下,如何完成这个需求? 直切主题 那这个时候我们可以使用动态代理 要理解动态代理首先要理解代理模式 什么是代 ...

  9. 设计模式——代理模式(附代码示例)

    一. 代理模式 1. 概念 代理模式为一个对象提供一个替身,以控制对这个对象的访问.即通过代理对象访问目标对象.这样做的好处是可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能.被代 ...

  10. 设计模式之-代理模式

    代理模式详解 特点: 1. 执行者. 被代理人 2. 对于被代理人来说, 这件事情是一定要做的, 但是我自己又不想做或者没有时间做, 找代理. 3. 需要获取到被代理的人个人资料. 4.关心过程 穷举 ...

最新文章

  1. mysql 编码 windows_修改mysql默认编码的方法(windows环境)
  2. 【redis】缓存击穿、雪崩、穿透
  3. 业务系统实现记住密码和自动登录功能
  4. STM32如何查找hardfault原因
  5. springboot入门demo详解(解决跨域)
  6. 非对称加密算法 - Java加密与安全
  7. 深入理解SpringBoot(4)——web开发
  8. Spark之RDD理论篇
  9. 售价扎心!索尼Xperia 5国行版开启预售:骁龙855+1200万后置三摄
  10. 目标检测——模型的快速验证
  11. 结构体符号重载(简单版+手动扩栈方法)
  12. 洛谷——P4053 [JSOI2007]建筑抢修
  13. prototype.js之$A(iterable)
  14. 2018-2019-1 20189204《Linux内核原理与分析》第二周作业
  15. java List的简单运用
  16. 面试最后一问:你有什么问题想问我吗?
  17. getch方法_c语言中getch的用法
  18. 统计学,机器学习,深度学习,数据挖掘的联系
  19. 输入五个城市从小到大排序-c语言
  20. 新海诚没有参与制作的作品_由新海诚创作,却成为冷门的一部动漫,至今都没看懂...

热门文章

  1. Facebook开源MySQL分支获大佬捧场
  2. ORA-24042 ORA-12545 ORA-26714 bug(Bug 5623403)
  3. [实验手册]用2500路由器做自动安装实验
  4. 解决Oracle监听器服务不能启动的问题
  5. Atlas 调用web service
  6. xmlhttp上传文件(转贴)
  7. cyhper study
  8. 理解Java的几张图
  9. 分享400多道算法题,来挑战吧
  10. 为什么 String hashCode 方法选择数字31作为乘子?