###代碼示例java
Java原生API中,動態代理經常使用的API有兩個:InvocationHandler接口和Proxy類數據庫
首先上代碼StaffLoggerAspect.javaexpress
public class StaffLoggerAspect implements InvocationHandler { Object target; public Object getObject(Object object) { target = object; return Proxy.newProxyInstance(Staff.class.getClassLoader(), Staff.class.getInterfaces(), this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println(proxy.getClass().getName()); return method.invoke(target, args); } }
Main類的main方法bootstrap
public static void main(String[] args) { StaffLoggerAspect aspect = new StaffLoggerAspect(); Staff staff1 = new Staff(); staff1.setName(""); System.out.println(staff1); Object staff2 = aspect.getObject(staff1); System.out.println(staff2); }
輸出結果安全
Staff{name='', age=0} com.sun.proxy.$Proxy0 Staff{name='', age=0}
###注意事項網絡
先來解釋getObject方法,不少網上的教程都有某種函數用來作相似的事情,這個函數用來返回一個與被代理對象實現了相同接口的代理對象dom
注意它返回的是代理對象,而不是原對象。代理對象是Proxy類的子類(API文檔),實現了被代理對象的全部接口,因此對這個函數結果進行強制轉換的話必須轉換成對應的接口類型而不是對象類型,對於沒有接口的對象怎麼辦呢?只能轉換成Object對象了,固然你可以經過代理使用的方法也只有Object自帶的equals, toString之類的了,一些Object方法不會觸發invoke方法,詳見後邊Proxy對象特徵最後一條。代理對象的類名是預留類名詳見Java API對於Proxy類的解釋ide
###Java API Proxy 部分原文及解釋函數
A proxy class has the following properties:性能
- Proxy classes are public, final, and not abstract. (即不可繼承)
- The unqualified name of a proxy class is unspecified. The space of class names that begin with the string "$Proxy" should be, however, reserved for proxy classes. (代理對象類名是沒有明肯定義的,可是以$Proxy開頭的類名要給代理對象保留着,因此標準狀況下若是發現某個class的類名以$Proxy開頭,那它確定是代理對象,具體可見輸出結果)
- A proxy class extends java.lang.reflect.Proxy. (代理對象全是這個類的子類,因此能夠用instanceof來判斷是不是代理對象)
- A proxy class implements exactly the interfaces specified at its creation, in the same order.(代理對象和被代理對象所實現接口徹底一致,連順序也一致,順序是作什麼用的呢?原文檔後邊有一節標題是Methods Duplicated in Multiple Proxy Interfaces,這個順序便是用來處理多接口具備相同函數聲明的狀況的,這裏不詳述)
- If a proxy class implements a non-public interface, then it will be defined in the same package as that interface. Otherwise, the package of a proxy class is also unspecified. Note that package sealing will not prevent a proxy class from being successfully defined in a particular package at runtime, and neither will classes already defined by the same class loader and the same package with particular signers.(好長一串,意思是:若是公共接口,那麼包名不肯定,即便是包的內部接口,其它包訪問不了,若是進行了代理,那麼包裏也會多出來這麼一個代理類。後面說的跟代理無關了,說的是使用反射動態建立對象的話,包密閉是防不住色狼類的,徹底阻止不了)
- Since a proxy class implements all of the interfaces specified at its creation, invoking getInterfaces on its Class object will return an array containing the same list of interfaces (in the order specified at its creation), invoking getMethods on its Class object will return an array of Method objects that include all of the methods in those interfaces, and invoking getMethod will find methods in the proxy interfaces as would be expected.(由於以前說的一些原理,因此代理對象的getInterfaces返回被代理對象全部接口,順序一致,代理對象的getMethod返回那些接口的方法)
- The Proxy.isProxyClass method will return true if it is passed a proxy class-- a class returned by Proxy.getProxyClass or the class of an object returned by Proxy.newProxyInstance-- and false otherwise.(代理對象兩種獲取方式:Proxy.getProxyClass或者Proxy.newProxyInstance其它方式獲取的Proxy.isProxyClass會返回false)
- The java.security.ProtectionDomain of a proxy class is the same as that of system classes loaded by the bootstrap class loader, such as java.lang.Object, because the code for a proxy class is generated by trusted system code. This protection domain will typically be granted java.security.AllPermission.(代理對象權限極高,具備java.security.AllPermission,和java.lang.Object及其它的啓動對象一致)
- Each proxy class has one public constructor that takes one argument, an implementation of the interface InvocationHandler, to set the invocation handler for a proxy instance. Rather than having to use the reflection API to access the public constructor, a proxy instance can be also be created by calling the Proxy.newProxyInstance method, which combines the actions of calling Proxy.getProxyClass with invoking the constructor with an invocation handler.(代理類具備一個public單參數構造函數,須要InvocationHandler對象做爲輸入參數)
- Given a proxy instance proxy and one of the interfaces implemented by its proxy class Foo, the following expression will return true:
proxy instanceof Foo
and the following cast operation will succeed (rather than throwing a ClassCastException):
(Foo) proxy
(首先要注意斷句,看英文的話implemented by後邊很容易搞錯,應該是its proxy class / Foo, 總體上是這樣的: Given a proxy instance proxy and one of its interfaces, for example Foo, ... Foo是接口,不是代理類的對象!!!根據以前proxy對象的說明,這一點自己沒什麼難理解的,它一加說明反而容易弄錯)
- Each proxy instance has an associated invocation handler, the one that was passed to its constructor. The static Proxy.getInvocationHandler method will return the invocation handler associated with the proxy instance passed as its argument.(廢話,就是說想要拿到某個代理對象的InvocationHandler的話調用Proxy.getInvocationHandler方法)
- An interface method invocation on a proxy instance will be encoded and dispatched to the invocation handler's invoke method as described in the documentation for that method. (代理對象上方法調用會發送到與之關聯的InvocationHandler對象的invoke方法,注意全部方法調用都會被轉發,須要在invoke裏判斷是否是你要弄的那個方法)
- An invocation of the hashCode, equals, or toString methods declared in java.lang.Object on a proxy instance will be encoded and dispatched to the invocation handler's invoke method in the same manner as interface method invocations are encoded and dispatched, as described above. The declaring class of the Method object passed to invoke will be java.lang.Object. Other public methods of a proxy instance inherited from java.lang.Object are not overridden by a proxy class, so invocations of those methods behave like they do for instances of java.lang.Object.(hashCode, equals, toString方法也會轉發到proxy對象InvocationHandler的invoke方法,其它沒有轉發!!!重要!!!要注意!!!)
文檔重要部分解釋完了,這裏補充一些實際使用時候用的上的包括思考方式等。
最開始我看到那個getObject裏target設置的不是Proxy對象有點轉不過來,其實要想清楚,過程是這樣的:
使用Java API動態代理的注意事項就簡單說到這裏,對於Method對象的使用和注意事項請自行查找相關API或教程,CGLib動態代理實現原理有所不一樣,類似性確定有,可是請注意和Java原生API動態代理的區別(Java原生經過反射直接生成getInterfaces()獲得的那些接口的一個對象,並無論被代理對象extends的部分,因此構造很是快,可是執行的時候性能低,由於須要各類轉發;CGLib經過反射直接生成被代理對象的子類,因此不可用於final類的代理,由於這種類不可被繼承,同時會把invoke的內容寫入被生成的代理對象裏,因此生成的時候會很慢,可是這種對象直接就是被代理對象相同類型的對象,畢竟是生成被代理類的子類,因此使用方便,被代理對象能夠不須要接口,並且執行方法速度很快。由於原理區別比較大,CGLib確定還有其它與Java API不一樣的特徵,筆者暫時沒有時間研究CGLib的特色,暫不詳述)。在選擇上,對於單例模式的對象,或者說相比於對象方法調用的次數,構造次數不多的對象建議用CGLib,對於須要大量構造(好比數據庫的一條記錄,網絡服務的一個Request對象),相對而言每一個對象方法調用次數不是不少的對象,建議使用Java原生API。
發現文中有不當之處,但願指正,尤爲CGLib和Java API比較部分,是從原理出發的一些推斷,並不肯定,建議你們去看CGLib的相關文檔。
筆者撰文不易,轉載請註明出處,拜謝