最近用到了java的動態代理,雖然會用,但不了解他具體是怎么實現,抽空看看了看他的源碼。
說到Java的動態代理就不能不說到代理模式,動態代理也就是多了一個’動態’兩字,在《大話設計模式》中不是有這句話嗎?“反射,反射程序員的快樂”,這里也不例外,他在底層也是使用了反射來創建對象。
一、 為了讓我們更加明白的了解動態代理,我們先來復習一下代理模式吧(沒有學過的,也得假裝復習是復習呀,不然掉面)。
public interface BookManager { void addBook();}
1 package com.test; 2 3 public class Library implements BookManager { 4 5 public void addBook() { 6 // TODO Auto-generated method stub 7 System.out. 1 package com.test; 2 3 public class Agent implements BookManager { 4 private BookManager library; 5 6 public BookManager getLibrary() { 7 return library; 8 } 9 10 public void setLibrary(BookManager library) {11 this.library = library;12 }13 14 public void addBook() {15 // TODO Auto-generated method stub16 System.out.println("添加圖書之前");17 library.addBook();18 System.out.println("添加圖書之后");19 }20 21 }測試代碼
package com.test;public class BookTest {public static void main(String[] args) {BookManager library = new Library();Agent agent = new Agent();agent.setLibrary(library);agent.addBook();}}執行結果為添加圖書之前增加圖書。。。。。添加圖書之后從這里可以看到在代理模式中要求是都實現了相同的接口,所以這樣的代碼,移植性不強,所以催生出動態代理。動態代理不要求,必須實現相同的接口,減少了代碼量。
先上代碼
public interface BookFacade {public void addBook(); }
public class BookFacadeImpl implements BookFacade,system {public void addBook() {// TODO Auto-generated method stubSystem.out.println("增加圖書方法。。。"); }public void doSys() {// TODO Auto-generated method stubSystem.out.println("dsfsdfsd");}}
public class BookFacadeProxy implements InvocationHandler {private Object target; /** * 綁定委托對象并返回一個代理類 * @param target * @return */ public Object bind(Object target) { this.target = target; //取得代理對象 return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this); //要綁定接口(這是一個缺陷,cglib彌補了這一缺陷) } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result=null; System.out.println("事物開始"); //執行方法 System.out.println("ClassLoad"+proxy.getClass().getSimpleName()); result=method.invoke(target, args); System.out.println("事物結束"); return result; } }
public class TestProxy {public static void main(String[] args) { BookFacadeProxy proxy = new BookFacadeProxy(); BookFacade bookProxy = (BookFacade) proxy.bind(new BookFacadeImpl()); bookProxy.addBook(); ProxyGeneratorUtils.writeProxyClassToHardDisk("F:/$Proxy11.class");}} 我先聲明后面這段代碼我盜用了其他網友的成果。
先說明用法,再來解釋為什么會是這樣的呢?
第一步 你必須得聲明一個接口,而且在目標類必須實現這個接口,不然你使用動態代理是不會成功的。
第二步 需要實現 InvocationHandler 接口,創建一個代理類,這個代理類里需要重寫invoke方法,在這個方法里,需要寫上多被代理對象的調用method.invoke(target, args),如果不知道method.invoke()是做什么用的,可以去看看反射就明白了。參數有兩個一個是被代理的對象,第二個就是調用該方法的參數
第三步 需要獲得代理對象,可以通過Object Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
方法來生成一個代理對象。
具體的第一個是使用那一個類加載器加載,第二個就是需要綁定的接口,第三個就是持有處理的對象。
測試方法安裝你正常調用相應的方法就行。
測試類執行結果如下
事物開始增加圖書方法。。。事物結束
現在我們知道用法了,按照我們一般考慮問題的思路是,
生成的代理對象,能調用接口的的方法,肯定是實現了接口這是我們的猜想一,這個涉及到代理對象的生成。
第二個調用addBook方法,能執行,invoke方法,他是怎么調用的呢。
那么我們現在就進入源碼吧,先來看看對象是怎么生成的。
Returns an instance of a proxy class for the specified interfaces that dispatches method invocations to the specified invocation handler.看看注釋就明白了,返回一個代理類的對象,而且還是實現了你進來接口的類public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException { if (h == null) { throw new NullPointerException(); } /* * 生成一個代理類 */ Class<?> cl = getProxyClass(loader, interfaces); /* * 通過反射用構造方法生成一個對象 */ try { Constructor cons = cl.getConstructor(constructorParams); return cons.newInstance(new Object[] { h }); } catch (NoSuchMethodException e) { throw new InternalError(e.toString()); } catch (IllegalaccessException e) { throw new InternalError(e.toString()); } catch (InstantiationException e) { throw new InternalError(e.toString()); } catch (InvocationTargetException e) { throw new InternalError(e.toString()); } }上面大家可以看到,生成了一個對象返回去了,這個對象就是我們所說的代理對象,那么我們可以看看這個class 是怎么生成?
public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces) throws IllegalArgumentException { 驗證接口的長度 if (interfaces.length > 65535) { throw new IllegalArgumentException("interface limit exceeded"); } 這個class 對象就是我們被返回對象的定義 Class<?> proxyClass = null; /* collect interface names to use as key for proxy class cache */ String[] interfaceNames = new String[interfaces.length]; // for detecting duplicates Set<Class<?>> interfaceSet = new HashSet<>(); for (int i = 0; i < interfaces.length; i++) { String interfaceName = interfaces[i].getName(); Class<?> interfaceClass = null; try { interfaceClass = Class.forName(interfaceName, false, loader); } catch (ClassNotFoundException e) { } if (interfaceClass != interfaces[i]) { throw new IllegalArgumentException( interfaces[i] + " is not visible from class loader"); } /* * Verify that the Class object actually represents an * interface. */ if (!interfaceClass.isInterface()) { throw new IllegalArgumentException( interfaceClass.getName() + " is not an interface"); } /* * Verify that this interface is not a duplicate. */ if (interfaceSet.contains(interfaceClass)) { throw new IllegalArgumentException( "repeated interface: " + interfaceClass.getName()); } interfaceSet.add(interfaceClass); interfaceNames[i] = interfaceName; } ....... 中間省略N多 生成一個代理class文件,這個就是我們要被返回的class 對象 /* * Generate the specified proxy class. */ byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces); try { proxyClass = defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length); } catch (ClassFormatError e) { /* 這樣我們就知道這個代理對象是怎么生成的。
接下來我們第2個問題就是這個invoke 方法是什么時候調用的。
byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces);
從這個方法我們可以看出來,他可以生成一個二進制的文件,那么我們把這個文件寫到本地就可以看到這個文件了。
public final class $Proxy110 extends Proxy implements BookFacade{ private static Method m1; private static Method m3; private static Method m0; private static Method m2; public $Proxy110(InvocationHandler paramInvocationHandler) throws { super(paramInvocationHandler); } public final boolean equals(Object paramObject) throws { try { return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue(); } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } public final void addBook() throws { try { this.h.invoke(this, m3, null); return; } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } }這個是生成
新聞熱點
疑難解答