java的靜態代理機制詳解。本站提示廣大學習愛好者:(java的靜態代理機制詳解)文章只能為提供參考,不一定能成為您想要的結果。以下是java的靜態代理機制詳解正文
在學習Spring的時分,我們知道Spring次要有兩大思想,一個是IoC,另一個就是AOP,關於IoC,依賴注入就不必多說了,而關於Spring的中心AOP來說,我們不但要知道怎樣經過AOP來滿足的我們的功用,我們更需求學習的是其底層是怎樣樣的一個原理,而AOP的原理就是java的靜態代理機制,所以本篇隨筆就是對java的靜態機制停止一個回憶。
在java的靜態代理機制中,有兩個重要的類或接口,一個是 InvocationHandler(Interface)、另一個則是 Proxy(Class),這一個類和接口是完成我們靜態代理所必需用到的。首先我們先來看看java的API協助文檔是怎樣樣對這兩個類停止描繪的:
InvocationHandler:
InvocationHandler is the interface implemented by the invocation handler of a proxy instance. Each proxy instance has an associated invocation handler. When a method is invoked on a proxy instance, the method invocation is encoded and dispatched to the invoke method of its invocation handler.
每一個靜態代理類都必需要完成InvocationHandler這個接口,並且每個代理類的實例都關聯到了一個handler,當我們經過代理對象調用一個辦法的時分,這個辦法的調用就會被轉發為由InvocationHandler這個接口的 invoke 辦法來停止調用。我們來看看InvocationHandler這個接口的獨一一個辦法 invoke 辦法:
Object invoke(Object proxy, Method method, Object[] args) throws Throwable
我們看到這個辦法一共承受三個參數,那麼這三個參數辨別代表什麼呢?
Object invoke(Object proxy, Method method, Object[] args) throws Throwable proxy: 指代我們所代理的那個真實對象 method: 指代的是我們所要調用真實對象的某個辦法的Method對象 args: 指代的是調用真實對象某個辦法時承受的參數
假如不是很明白,等下經過一個實例會對這幾個參數停止更深的解說。
接上去我們來看看Proxy這個類:
Proxy provides static methods for creating dynamic proxy classes and instances, and it is also the superclass of all dynamic proxy classes created by those methods.
Proxy這個類的作用就是用來靜態創立一個代理對象的類,它提供了許多的辦法,但是我們用的最多的就是 newProxyInstance 這個辦法:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
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 loader: 一個ClassLoader對象,定義了由哪個ClassLoader對象來對生成的代理對象停止加載 interfaces: 一個Interface對象的數組,表示的是我將要給我需求代理的對象提供一組什麼接口,假如我提供了一組接口給它,那麼這個代理對象就聲稱完成了該接口(多態),這樣我就能調用這組接口中的辦法了 h: 一個InvocationHandler對象,表示的是當我這個靜態代理對象在調用辦法的時分,會關聯到哪一個InvocationHandler對象上
好了,在引見完這兩個接口(類)當前,我們來經過一個實例來看看我們的靜態代理形式是什麼樣的:
首先我們定義了一個Subject類型的接口,為其聲明了兩個辦法:
public interface Subject { public void rent(); public void hello(String str); }
接著,定義了一個類來完成這個接口,這個類就是我們的真實對象,RealSubject類:
public class RealSubject implements Subject { @Override public void rent() { System.out.println("I want to rent my house"); } @Override public void hello(String str) { System.out.println("hello: " + str); } }
下一步,我們就要定義一個靜態代理類了,後面說個,每一個靜態代理類都必需要完成 InvocationHandler 這個接口,因而我們這個靜態代理類也不例外:
public class DynamicProxy implements InvocationHandler { // 這個就是我們要代理的真實對象 private Object subject; // 結構辦法,給我們要代理的真實對象賦初值 public DynamicProxy(Object subject) { this.subject = subject; } @Override public Object invoke(Object object, Method method, Object[] args) throws Throwable { // 在代理真實對象前我們可以添加一些自己的操作 System.out.println("before rent house"); System.out.println("Method:" + method); // 當代理對象調用真實對象的辦法時,其會自動的跳轉到代理對象關聯的handler對象的invoke辦法來停止調用 method.invoke(subject, args); // 在代理真實對象後我們也可以添加一些自己的操作 System.out.println("after rent house"); return null; } }
最後,來看看我們的Client類:
public class Client { public static void main(String[] args) { // 我們要代理的真實對象 Subject realSubject = new RealSubject(); // 我們要代理哪個真實對象,就將該對象傳出來,最後是經過該真實對象來調用其辦法的 InvocationHandler handler = new DynamicProxy(realSubject); /* * 經過Proxy的newProxyInstance辦法來創立我們的代理對象,我們來看看其三個參數 * 第一個參數 handler.getClass().getClassLoader() ,我們這裡運用handler這個類的ClassLoader對象來加載我們的代理對象 * 第二個參數realSubject.getClass().getInterfaces(),我們這裡為代理對象提供的接口是真實對象所實行的接口,表示我要代理的是該真實對象,這樣我就能調用這組接口中的辦法了 * 第三個參數handler, 我們這裡將這個代理對象關聯到了上方的 InvocationHandler 這個對象上 */ Subject subject = (Subject)Proxy.newProxyInstance(handler.getClass().getClassLoader(), realSubject .getClass().getInterfaces(), handler); System.out.println(subject.getClass().getName()); subject.rent(); subject.hello("world"); } }
我們先來看看控制台的輸入:
$Proxy0
before rent house Method:public abstract void com.xiaoluo.dynamicproxy.Subject.rent() I want to rent my house after rent house
before rent house Method:public abstract void com.xiaoluo.dynamicproxy.Subject.hello(java.lang.String) hello: world after rent house
我們首先來看看 $Proxy0 這東西,我們看到,這個東西是由 System.out.println(subject.getClass().getName()); 這條語句打印出來的,那麼為什麼我們前往的這個代理對象的類名是這樣的呢?
Subject subject = (Subject)Proxy.newProxyInstance(handler.getClass().getClassLoader(), realSubject .getClass().getInterfaces(), handler);
能夠我以為前往的這個代理對象會是Subject類型的對象,或許是InvocationHandler的對象,後果卻不是,首先我們解釋一下為什麼我們這裡可以將其轉化為Subject類型的對象?緣由就是在newProxyInstance這個辦法的第二個參數上,我們給這個代理對象提供了一組什麼接口,那麼我這個代理對象就會完成了這組接口,這個時分我們當然可以將這個代理對象強迫類型轉化為這組接口中的恣意一個,由於這裡的接口是Subject類型,所以就可以將其轉化為Subject類型了。
同時我們一定要記住,經過 Proxy.newProxyInstance 創立的代理對象是在jvm運轉時靜態生成的一個對象,它並不是我們的InvocationHandler類型,也不是我們定義的那組接口的類型,而是在運轉是靜態生成的一個對象,並且命名方式都是這樣的方式,以$掃尾,proxy為中,最後一個數字表示對象的標號。
接著我們來看看這兩句
subject.rent();
subject.hello("world");
這裡是經過代理對象來調用完成的那種接口中的辦法,這個時分順序就會跳轉到由這個代理對象關聯到的 handler 中的invoke辦法去執行,而我們的這個 handler 對象又承受了一個 RealSubject類型的參數,表示我要代理的就是這個真實對象,所以此時就會調用 handler 中的invoke辦法去執行:
public Object invoke(Object object, Method method, Object[] args) throws Throwable { // 在代理真實對象前我們可以添加一些自己的操作 System.out.println("before rent house"); System.out.println("Method:" + method); // 當代理對象調用真實對象的辦法時,其會自動的跳轉到代理對象關聯的handler對象的invoke辦法來停止調用 method.invoke(subject, args); // 在代理真實對象後我們也可以添加一些自己的操作 System.out.println("after rent house"); return null; }
我們看到,在真正經過代理對象來調用真實對象的辦法的時分,我們可以在該辦法前後添加自己的一些操作,同時我們看到我們的這個 method 對象是這樣的:
public abstract void com.xiaoluo.dynamicproxy.Subject.rent() public abstract void com.xiaoluo.dynamicproxy.Subject.hello(java.lang.String)
正好就是我們的Subject接口中的兩個辦法,這也就證明了當我經過代理對象來調用辦法的時分,起實踐就是委托由其關聯到的 handler 對象的invoke辦法中來調用,並不是自己來真實調用,而是經過代理的方式來調用的。
這就是我們的java靜態代理機制
本篇隨筆詳細的解說了java中的靜態代理機制,這個知識點十分十分的重要,包括我們Spring的AOP其就是經過靜態代理的機制完成的,所以我們必需要好好的了解靜態代理的機制。