从mybatis源码看JDK动态代理
2020-04-06 16:04:02来源:博客园 阅读 ()
从mybatis源码看JDK动态代理
网上好多说到动态代理的文章内容都是这样子的:
一个实际干事的类Real;一个被创造的代理类Proxy。
Proxy调用Real中被代理的方法;有模有样的在被代理的方法前后打印出一些字符串。
比如下面的例子:
1 public class JdkProxy { 2 static interface IProxy{ 3 String say(String s); 4 } 5 static class Real implements IProxy{ 6 @Override 7 public String say(String s) { 8 System.out.println("说完了,返回结果"); 9 return s; 10 } 11 } 12 13 static class MyInvocationHandler implements InvocationHandler{ 14 private Object real; 15 16 public MyInvocationHandler(Object real) { 17 this.real = real; 18 } 19 20 @Override 21 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 22 Object res=null; 23 if(method.getName().equals("say")){ 24 System.out.println("say start..."); 25 res=method.invoke(real,args); 26 System.out.println("say end..."); 27 } 28 return res; 29 } 30 } 31 32 public static void main(String[] args) { 33 execu1(); 34 } 35 36 private static void execu1(){ 37 System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true"); 38 Real real=new Real(); 39 IProxy proxy=(IProxy) Proxy.newProxyInstance(real.getClass().getClassLoader(), real.getClass().getInterfaces(),new MyInvocationHandler(real)); 40 String s=proxy.say("abc"); 41 System.out.println(s); 42 } 43 44 }
上面21-27行代码是调用被代理的方法;
如果我现在不调用被代理的方法,而是直接写一个方法体。
代码如下:
1 public class JdkProxy { 2 static interface IProxy{ 3 String say(String s); 4 } 5 static class Real implements IProxy{ 6 @Override 7 public String say(String s) { 8 System.out.println("说完了,返回结果"); 9 return s; 10 } 11 } 12 13 static class MyInvocationHandler implements InvocationHandler{ 14 private Object real; 15 16 public MyInvocationHandler(Object real) { 17 this.real = real; 18 } 19 20 @Override 21 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 22 return "我什么也不代理,我直接就是一个方法"; 23 } 24 } 25 26 public static void main(String[] args) { 27 execu1(); 28 } 29 30 private static void execu1(){ 31 System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true"); 32 Real real=new Real(); 33 IProxy proxy=(IProxy) Proxy.newProxyInstance(IProxy.class.getClassLoader(),new Class[]{IProxy.class},new MyInvocationHandler(real)); 34 String s=proxy.say("abc"); 35 System.out.println(s); 36 } 37 38 }
改动代码是22行的代码。
如果不需要被代理的方法了,那么还需要实际干活的类吗?
继续修改代码:
1 public class JdkProxy { 2 static interface IProxy{ 3 String say(String s); 4 } 5 6 static class MyInvocationHandler implements InvocationHandler{ 7 8 9 @Override 10 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 11 return "我什么也不代理,我直接就是一个方法"; 12 } 13 } 14 15 public static void main(String[] args) { 16 execu1(); 17 } 18 19 private static void execu1(){ 20 System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true"); 21 IProxy proxy=(IProxy) Proxy.newProxyInstance(IProxy.class.getClassLoader(),new Class[]{IProxy.class},new MyInvocationHandler()); 22 String s=proxy.say("abc"); 23 System.out.println(s); 24 } 25 }
上面的程序依然能够正常运行。
从这个层面来说,动态代理就是给我们创造了一个类,至于有没有实际干活的类无关。
网上到处都在说mybatis接口编程的用的是动态代理,创造代理类我们很好理解,那么他到底代理了什么类呢?
先来个简单的mybatis例子:
1 SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml")); 2 SqlSession sqlSession= sqlSessionFactory.openSession(); 3 IRoleMapper iRoleMapper=sqlSession.getMapper(IRoleMapper.class); 4 List<Role> list=iRoleMapper.getRole(1L);
第三行代码就应该是获取代理的过程;iRoleMapper指向具体的代理类。
下面查看源码:
getMapper()源码:
1 public <T> T getMapper(Class<T> type, SqlSession sqlSession) { 2 final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type); 3 if (mapperProxyFactory == null) { 4 throw new BindingException("Type " + type + " is not known to the MapperRegistry."); 5 } 6 try { 7 return mapperProxyFactory.newInstance(sqlSession); 8 } catch (Exception e) { 9 throw new BindingException("Error getting mapper instance. Cause: " + e, e); 10 } 11 }
代码比较简洁,代理类是在第7行生成的。进入newInstance()方法:
newInstance()源码:
1 protected T newInstance(MapperProxy<T> mapperProxy) { 2 return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy); 3 } 4 5 public T newInstance(SqlSession sqlSession) { 6 final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache); 7 return newInstance(mapperProxy); 8 }
第1行的newInstance调用的是JDK的代理方法
第5行的newInstance是Mybatis自己的方法,根据JDK动态代理的调用规则,第6行的MapperProxy一定继承了InvocationHandler接口,并且实现了接口方法invoke();
invoke()源码
1 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 2 try { 3 if (Object.class.equals(method.getDeclaringClass())) { 4 return method.invoke(this, args); 5 } else if (method.isDefault()) { 6 if (privateLookupInMethod == null) { 7 return invokeDefaultMethodJava8(proxy, method, args); 8 } else { 9 return invokeDefaultMethodJava9(proxy, method, args); 10 } 11 } 12 } catch (Throwable t) { 13 throw ExceptionUtil.unwrapThrowable(t); 14 } 15 final MapperMethod mapperMethod = cachedMapperMethod(method); 16 return mapperMethod.execute(sqlSession, args); 17 }
invoke()方法体中并没有调用 res=method.invoke(real,args);这类的代码。说明一个问题,这里并没有我们常说的Real这样的类。
总结:
1.JDK动态代理只是需要一个接口,体的干活类不是必须的;至于具体做什么,由程序员在invoke()中去实现。
2.Mybatis用了JDK动态代理,但是并没有提供被代理的类;只是提供了一个代理类或者干脆说提供了一个我们看不见的类用于执行Sql
原文链接:https://www.cnblogs.com/guoyansi19900907/p/12641740.html
如有疑问请与原作者联系
标签:
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有
下一篇:注解与反射-复习
- MyBatis中的$和#,用不好,准备走人! 2020-06-11
- jdk各个版本下载 2020-06-11
- SpringBoot 2.3 整合最新版 ShardingJdbc + Druid + MyBatis 2020-06-11
- 你说研究过Spring里面的源码,循环依赖你会么? 2020-06-09
- 通俗理解spring源码(六)—— 默认标签(import、alias、be 2020-06-07
IDC资讯: 主机资讯 注册资讯 托管资讯 vps资讯 网站建设
网站运营: 建站经验 策划盈利 搜索优化 网站推广 免费资源
网络编程: Asp.Net编程 Asp编程 Php编程 Xml编程 Access Mssql Mysql 其它
服务器技术: Web服务器 Ftp服务器 Mail服务器 Dns服务器 安全防护
软件技巧: 其它软件 Word Excel Powerpoint Ghost Vista QQ空间 QQ FlashGet 迅雷
网页制作: FrontPages Dreamweaver Javascript css photoshop fireworks Flash