什麼是代理呢,其實很好理解,就是不直接訪問目標,而是經過一箇中間層來訪問,就好像下面這樣:java
舉個例子,若是咱們一些水果,好比:香蕉、蘋果等,寫成Java代碼,大概是下面這個樣子:git
//Fruit.java /** * 水果的接口 */ public interface Fruit { /** * 獲取水果的名字 */ public String getName(); } //Apple.java public class Apple implements Fruit { @Override public String getName() { return "蘋果"; } } //Banana.java public class Banana implements Fruit { @Override public String getName() { return "香蕉"; } }
可是吃水果,你要削皮吧,你不能每一個水果都寫一個子類,類處理削皮這個事情吧。所以,咱們能夠作一個代理 ,吃蘋果以前,先把它削皮。 就像下面這樣,把原來的水果包一層:github
//PeelFruitProxy.java /** * 代理,讓每一個水果去皮 */ public class PeelFruitProxy implements Fruit { private Fruit mFruit; public PeelFruit(Fruit fruit) { this.mFruit = fruit; } @Override public String getName() { System.out.println("proxt:" + proxy.getClass().getName()); return "去皮的" + mFruit.getName(); } }
添加了測試類,測試類以下:服務器
//Main.java public class Main { public static void main(String[] args) { Apple apple=new Apple();//原始的蘋果 Banana banana=new Banana();//原始的香蕉 PeelFruitProxy peelApple=new PeelFruitProxy(apple);//代理,添加去皮功能 PeelFruitProxy peelBanana=new PeelFruitProxy(banana);//代理,添加去皮功能 System.out.println(peelApple.getName()); System.out.println(peelBanana.getName()); } }
以上就是Java的靜態代理,簡單點說,就是把原來的目標對象包一層,加入新東西再去調用目標自己。 可是若是隻是這樣的靜態代理,一個接口,就須要一個代理,實現起來是否是很繁瑣。微信
在Java中,有一個幹這個事情的類,叫作Proxy
,能夠直接使用反射方式,代理攔截。 先簡單的介紹一下這個類,其實最經常使用的只有一個靜態方法Proxt.newProxyInstance()
,是這樣的:app
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
首先咱們要實現InvocationHandler,實現其中的invoke方法,在調用目標對象的時候,會先調用到invoke方法,須要實現者在這個方法中,在主動調用被調用者方法。框架
//FruitInvocationHandler.java /** * 調用方法攔截器 */ public class FruitInvocationHandler implements InvocationHandler { private Fruit mFruit; public FruitInvocationHandler(Fruit fruit) { this.mFruit = fruit; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String result = (String) method.invoke(mFruit, args);//須要在這個方法裏面,主動調用被代理的對象。 return "去皮的" + result; } }
運行一下:ide
//Main.Java public class Main { public static void main(String[] args) { Apple apple = new Apple(); Fruit proxyApple = (Fruit) Proxy.newProxyInstance(Fruit.class.getClassLoader(), new Class[]{Fruit.class}, new FruitInvocationHandler(apple)); System.out.println(proxyApple.getClass().getName()); System.out.println(proxyApple.getName()); Banana banana = new Banana(); Fruit proxyBanana = (Fruit) Proxy.newProxyInstance(Fruit.class.getClassLoader(), new Class[]{Fruit.class}, new FruitInvocationHandler(banana)); System.out.println(proxyApple.getClass().getName()); System.out.println(proxyBanana.getName()); } }
這個方法,就是生成一個上文中的PeelFruitProxy
(固然,咱們看到的他名字叫:com.sun.proxy.$Proxy0),動態的生成,避免每次都須要寫,這個也是叫他動態代理的緣由,由於咱們能夠在運行時代理任意類。 不少程序中的AOP就是這樣實現的,可是咱們發現一些特色,newProxyInstance()的第二個參數,是一個interfaces的列表,爲啥要有這個這個列表呢?學習
由於咱們動態生成的代理類,也須要實現接口,這樣才方便向下轉型,使用其中的方法,否則,生成的類,類名就是com.sun.proxy.$Proxy0這種,而且是在內存中,沒法調用生成的方法。 ** 因此,這種動態代理的方法,有一個致命的缺點,那就是被代理的類,必需要實現接口。**測試
cglib is a powerful, high performance and quality Code Generation Library, It is used to extend JAVA classes and implements interfaces at runtime.
另外一個大名鼎鼎的Java代理實現,就是CGLib(Code Generation Library),一個基於ASM的代碼生成框架,能夠用他來動態生成類,而後實現對方法的攔截,就能夠避開JDK的動態代理, 必需要目標類實現接口的問題了。 也就是說,能夠用CGLib來生成上文中的PeelFruitProxy
。
簡單介紹一下怎麼用,首先這個CGLib是一個三方的庫,咱們要把它依賴進來:
compile 'cglib:cglib:3.2.8'
最新版本能夠在這裏看(新版本)[https://github.com/cglib/cglib/releases] 而後咱們來試一試,咱們來實現一下上面的代理
//FruitMethodInterceptor.java /** * CGLib代理的方法攔截器 */ public class FruitMethodInterceptor implements MethodInterceptor{ @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { String result = (String) proxy.invokeSuper(obj, args);//主要,這裏調用的是父類,也就是說, 生成的類和原始類是繼承關係 return "去皮的"+result; } } //Main.java public class Main { public static void main(String[] args) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(Apple.class); enhancer.setCallback(new FruitMethodInterceptor()); Apple apple = (Apple) enhancer.create(); System.out.println(apple.getClass().getName()); System.out.println(apple.getName()); } }
運行效果以下:
咱們看到,實現了一樣的功能,可是,Apple已經不是原來的Apple類了,變成了com.zjiecode.learn.java.proxy.Apple$$EnhancerByCGLIB$$44ade224
,沒錯,咱們正真使用的是這個類,而不是原來的Apple
了,這個類繼承自Apple,最後實現了對Apple類的代理。 這種方式,由於使用的是繼承,因此,無需被代理的類實現接口。固然,他也能夠經過接口來實現代理。
紙上談來終覺淺,絕知此事要躬行。 文中提到的源代碼,你均可以參考: https://github.com/zjiecode/learn-java/tree/feature/java-proxy
https://blog.csdn.net/danchu/article/details/70238002 https://blog.csdn.net/lovejj1994/article/details/78080124 https://www.jianshu.com/p/9a61af393e41?from=timeline&isappinstalled=0 https://github.com/cglib/cglib/wiki
做爲我的開發者,不少時候須要關注服務器報警,提醒等, 要直接把服務器消息推送到手機上,爲此,咱們開發了微信消息推送服務[wxpusher],能夠用過API直接把消息實時push到微信上,點擊查看介紹。
有木有用 ,關注一下唄,萬一有須要的時候呢?