網(wǎng)上好多說到動態(tài)代理的文章內(nèi)容都是這樣子的: 一個實(shí)際干事的類Real;一個被創(chuàng)造的代理類Proxy。 Proxy調(diào)用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("說完了,返回結(jié)果"); 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行代碼是調(diào)用被代理的方法; 如果我現(xiàn)在不調(diào)用被代理的方法,而是直接寫一個方法體。 代碼如下: 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("說完了,返回結(jié)果"); 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行的代碼。 如果不需要被代理的方法了,那么還需要實(shí)際干活的類嗎? 繼續(xù)修改代碼: 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 } 上面的程序依然能夠正常運(yùn)行。 從這個層面來說,動態(tài)代理就是給我們創(chuàng)造了一個類,至于有沒有實(shí)際干活的類無關(guān)。 網(wǎng)上到處都在說mybatis接口編程的用的是動態(tài)代理,創(chuàng)造代理類我們很好理解,那么他到底代理了什么類呢? 先來個簡單的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); 第三行代碼就應(yīng)該是獲取代理的過程;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行生成的。進(jìn)入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調(diào)用的是JDK的代理方法 第5行的newInstance是Mybatis自己的方法,根據(jù)JDK動態(tài)代理的調(diào)用規(guī)則,第6行的MapperProxy一定繼承了InvocationHandler接口,并且實(shí)現(xiàn)了接口方法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()方法體中并沒有調(diào)用 res=method.invoke(real,args);這類的代碼。說明一個問題,這里并沒有我們常說的Real這樣的類。 總結(jié): 1.JDK動態(tài)代理只是需要一個接口,至于實(shí)際干活類不是必須的;至于具體做什么,由程序員在invoke()中去實(shí)現(xiàn)。 2.Mybatis用了JDK動態(tài)代理,但是并沒有提供被代理的類;只是提供了一個代理類或者干脆說提供了一個我們看不見的類用于執(zhí)行Sql
|
|