項(xiàng)目中常用到代理模式,本篇文章帶大家回顧一下基本的操作以及動(dòng)態(tài)代理生成的類。
1. 定義
代理模式: 為其它對(duì)象提供代理,帶你對(duì)象挾持原對(duì)象類的引用,也稱委托模式。
作用:可以在不修改原對(duì)象的功能前提下,對(duì)原對(duì)象在功能進(jìn)行擴(kuò)展。(通俗講就是在當(dāng)你原對(duì)象封裝完畢或者你沒辦法修改,但是有一些增加的新的功能的時(shí)候,就可以在代理類上增加 符合“開閉原則”)
2. 代理的方式
-
靜態(tài)代理
使用:手動(dòng)創(chuàng)建源代碼,在對(duì)其編譯。
缺點(diǎn):代理類跟原對(duì)象類實(shí)現(xiàn)一樣的接口,所以會(huì)有很多代理類。并且,當(dāng)接口增加方法,目標(biāo)對(duì)象與代理對(duì)象都要對(duì)應(yīng)修改。(怎么解決?改用動(dòng)態(tài)代理。)
-
動(dòng)態(tài)代理
使用:在程序運(yùn)行時(shí),利用反射機(jī)制動(dòng)態(tài)創(chuàng)建。
3. 基本方式
-
靜態(tài)代理的使用方式
(下面以高低端電腦的例子展示一下基本使用方式)
image.png
- 定義接口
/**
* Create by ldr
* on 2019/12/10 15:06.
* 定義接口,電腦接口
*/
public interface Computer {
void screen();//顯示器
void memoryBank();//內(nèi)存條
void mainBoard();//主板
void graphicsCard();//顯卡
}
- 分別定義兩個(gè)被代理對(duì)象
------------------------------------------高配版電腦---------------------------------------------------
/**
* Create by ldr
* on 2019/12/10 15:08.
* 高配電腦
*/
public class HeightConfigurationComputer implements Computer{
private static final String TAG = "HeightComp";
@Override
public void screen() {
System.out.println("高配置電腦 -- 屏幕");
}
@Override
public void memoryBank() {
System.out.println("高配置電腦 -- 內(nèi)存條");
}
@Override
public void mainBoard() {
System.out.println("高配置電腦 -- 主板");
}
@Override
public void graphicsCard() {
System.out.println("高配置電腦 -- 顯卡");
}
}
------------------------------------------低配版電腦---------------------------------------------------
/**
* Create by ldr
* on 2019/12/10 15:08.
* 低配電腦
*/
public class LowConfigurationComputer implements Computer {
private static final String TAG = "LowComputer";
@Override
public void screen() {
System.out.println("低配置電腦 -- 屏幕");
}
@Override
public void memoryBank() {
System.out.println("低配置電腦 -- 內(nèi)存條");
}
@Override
public void mainBoard() {
System.out.println("低配置電腦 -- 主板");
}
@Override
public void graphicsCard() {
System.out.println("低配置電腦 -- 顯卡");
}
}
- 創(chuàng)建靜態(tài)代理類
/**
* Create by ldr
* on 2019/12/10 15:14.
* 靜態(tài)代理
*/
public class ComputerStaticPro implements Computer {
private Computer mComputer;
public void setProx(Computer computer){
mComputer = computer;
}
@Override
public void screen() {
System.out.println("代理類可先做共用一些邏輯~");
mComputer.screen();
}
@Override
public void memoryBank() {
System.out.println("代理類可先做共用一些邏輯~");
mComputer.memoryBank();
}
@Override
public void mainBoard() {
System.out.println("代理類可先做共用一些邏輯~");
mComputer.mainBoard();
}
@Override
public void graphicsCard() {
System.out.println("代理類可先做共用一些邏輯~");
mComputer.graphicsCard();
}
}
- 使用方式
HeightConfigurationComputer heightConfigurationComputer = new HeightConfigurationComputer();
ComputerStaticPro staticPro = new ComputerStaticPro();
staticPro.setProx(heightConfigurationComputer);
staticPro.graphicsCard();
staticPro.mainBoard();
staticPro.screen();
staticPro.memoryBank();
//低配電腦
staticPro.setProx(new LowConfigurationComputer());
staticPro.graphicsCard();
staticPro.mainBoard();
staticPro.screen();
staticPro.memoryBank();
-----------------------打印出來(lái)的Log
代理類可先做共用一些邏輯~
高配置電腦 -- 顯卡
代理類可先做共用一些邏輯~
高配置電腦 -- 主板
代理類可先做共用一些邏輯~
高配置電腦 -- 屏幕
代理類可先做共用一些邏輯~
高配置電腦 -- 內(nèi)存條
代理類可先做共用一些邏輯~
低配置電腦 -- 顯卡
代理類可先做共用一些邏輯~
低配置電腦 -- 主板
代理類可先做共用一些邏輯~
低配置電腦 -- 屏幕
代理類可先做共用一些邏輯~
低配置電腦 -- 內(nèi)存條
- 動(dòng)態(tài)代理的使用方式
- 創(chuàng)建動(dòng)態(tài)代理類
/**
* Create by ldr
* on 2019/12/10 15:55.
* 動(dòng)態(tài)代理
*/
public class ComputerDynamicPro implements InvocationHandler {
//動(dòng)態(tài)代理類需要被代理的實(shí)例
private Computer mComputer;
public ComputerDynamicPro(Computer computer){
mComputer = computer;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//關(guān)鍵點(diǎn)1
//執(zhí)行被代理的方法的類 方法被調(diào)用時(shí)就會(huì)自動(dòng)調(diào)用
Object invoke = method.invoke(mComputer,args);
//關(guān)鍵點(diǎn)2
String name = method.getName();
if (name.equals("screen")){
System.out.println(mComputer.getClass().getSimpleName()+" screen()" );
}else if (name.equals("memoryBank")){
System.out.println(mComputer.getClass().getSimpleName()+" memoryBank()" );
}else if (name.equals("mainBoard")){
System.out.println(mComputer.getClass().getSimpleName()+" mainBoard()" );
}else if(name.equals("graphicsCard")){
System.out.println(mComputer.getClass().getSimpleName()+" graphicsCard()" );
}
return invoke;
}
}
動(dòng)態(tài)代理需要實(shí)現(xiàn)的是
InvocationHandler接口,并重寫invoke方法,同樣也是要挾持被代理類的對(duì)象。
在invoke方法中可以看到,使用的method.invoke(mComputer,args);傳入對(duì)象跟args便可以實(shí)現(xiàn)方法被調(diào)用時(shí)自動(dòng)調(diào)用。在這個(gè)地方,其實(shí)你可以做的事情很多,比如你想知道方式的執(zhí)行時(shí)間,你可以在
method.invoke()的關(guān)鍵點(diǎn)1跟關(guān)鍵點(diǎn)2,加上時(shí)間,根據(jù)兩個(gè)時(shí)間差來(lái)計(jì)算一下這個(gè)方法的大致運(yùn)行時(shí)間。(實(shí)現(xiàn)AOP(面向程序切面編程))
- 使用
//關(guān)鍵代碼1
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
// //使用動(dòng)態(tài)代理
// //高配電腦
HeightConfigurationComputer heightConfigurationComputer = new HeightConfigurationComputer();
ClassLoader classLoader = heightConfigurationComputer.getClass().getClassLoader();
Class[] interfaces = heightConfigurationComputer.getClass().getInterfaces();
ComputerDynamicPro computerDynamicPro = new ComputerDynamicPro(heightConfigurationComputer);
//通過(guò)Proxy.newProxyInstance生成代理類對(duì)象
// 關(guān)鍵代碼2
Object newProxyInstance = Proxy.newProxyInstance(classLoader,interfaces,computerDynamicPro);
Computer computer = (Computer) newProxyInstance;
computer.graphicsCard();
computer.mainBoard();
computer.memoryBank();
computer.screen();
關(guān)鍵代碼1:是看人家網(wǎng)上說(shuō)的可以將代理類生成的一句代碼,等下再貼一下代理類的代碼
關(guān)鍵代碼2:這里可以看到Proxy.newProxyInstance傳入了三個(gè)參數(shù)。第一個(gè)參數(shù)
ClassLoader loader:被代理的類的類加載器,通過(guò)它生成代理的class文件。第二個(gè)參數(shù)
Class<?>[] interfaces:被代理類實(shí)現(xiàn)的所有接口字節(jié)碼,代理類需要知道并實(shí)現(xiàn)第三個(gè)參數(shù)
InvocationHandler h:調(diào)用處理程序調(diào)度方法調(diào)用,也就是第一步我們定義好的那個(gè)ComputerDynamicPro實(shí)現(xiàn)了InvocationHandler接口的動(dòng)態(tài)代理類。
我們來(lái)看一下關(guān)鍵代碼1中為我們生成的代理類的代碼吧
package com.sun.proxy;
import com.mzs.aptpro.Computer;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class $Proxy0 extends Proxy implements Computer {
private static Method m1;
private static Method m5;
private static Method m2;
private static Method m3;
private static Method m6;
private static Method m0;
private static Method m4;
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final void graphicsCard() throws {
try {
super.h.invoke(this, m5, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final void mainBoard() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final void screen() throws {
try {
super.h.invoke(this, m6, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final void memoryBank() throws {
try {
super.h.invoke(this, m4, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m5 = Class.forName("com.mzs.aptpro.Computer").getMethod("graphicsCard");
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("com.mzs.aptpro.Computer").getMethod("mainBoard");
m6 = Class.forName("com.mzs.aptpro.Computer").getMethod("screen");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
m4 = Class.forName("com.mzs.aptpro.Computer").getMethod("memoryBank");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
可以看到上面
class $Proxy0 extends Proxy implements Computer也是要實(shí)現(xiàn)我們的接口。在代碼最下面可以看到靜態(tài)代碼塊中,使用了反射將所有方法獲取到。
每個(gè)方法中都調(diào)用super.h.invoke()這樣的一個(gè)代碼 并且傳入三個(gè)參數(shù)this,反射獲取的方法,參數(shù)數(shù)組。
super.h實(shí)際上父類的h也是$Proxy0中的InvocationHandler,在構(gòu)造函數(shù)的時(shí)候傳遞上去的public $Proxy0(InvocationHandler var1) throws { super(var1); }。
這里你就可以明白了為什么我們要?jiǎng)討B(tài)代理類要實(shí)現(xiàn)InvocationHandler并實(shí)現(xiàn)invoke方法,并且inovke方法的三個(gè)參數(shù)是什么時(shí)候傳遞進(jìn)來(lái)的。
總結(jié)一下:
靜態(tài)代理跟動(dòng)態(tài)代理的區(qū)別:
靜態(tài)代理:需要自己手動(dòng)生成類文件,并且被代理類跟代理類都需要實(shí)現(xiàn)同樣的接口,當(dāng)接口改變時(shí)代理類跟被代理類都需要修改。
動(dòng)態(tài)代理:只需實(shí)現(xiàn)InvocationHandler,并重寫invoke方法。不需要知道也用不著實(shí)現(xiàn)接口,當(dāng)接口改變的時(shí)候也只需要改變invoke中的邏輯便可以。
