代理模式
學習目標
- 掌握代理模式的應用場景和實作原理,
- 應用場景
- 保護目標物件:客戶并不知道物件是如何實作具體業務功能的,只要呼叫代理物件的介面就行了,
- 增強目標物件:在實作目標物件的業務功能之前或者之后做一些業務實作,
Spring-AOP
- 實作原理
- 靜態代理:代理物件持有被代理物件的參考,由被代理物件實作目標業務,
- 動態代理:代理物件持有被代理物件的參考,代碼運行產生一個新的
java類,并被編譯重新加載,然后在新的類中執行目標業務實作動態代理,
- 應用場景
- 了解靜態代理和動態代理的區別,
- 靜態代理:被代理物件在代碼中被寫死了,不易擴展,適用于簡單的業務,代碼復用性不高,違背開閉原則,
- 動態代理:只要是實作了同一個介面的被代理類,都可以代理實作,實作了業務擴展,代碼復用,遵循開閉原則,
- 了解
CGLib和JDK-proxy的區別,JDK-proxy動態代理:實作了被代理物件的介面,生成新的class位元組碼,利用反射呼叫實作代理,無法代理不實作代理業務介面的方法,CGLib動態代理:繼承被代理物件,生成新的class位元組碼,通過fastclass機制呼叫實作代理,無法代理被final關鍵字修飾的方法
代理模式的定義
為其他物件提供一種代理,以控制對這個物件的訪問,代理物件在客戶端和服務端起到中介的作用,是一種結構型設計模式,
靜態代理
/**
* @description: 被代理物件需要實作的目標介面
* @author: lmc
* @create: 2019-06-12 15:38
**/
public interface ITarget {
/**
* @description: 被代理物件的行為
* @return void
* @date 2019/6/12 15:39
* @author lmc
*/
void behavior();
}
/**
* @description: 真實的被代理的目標物件
* @author: lmc
* @create: 2019-06-12 15:41
**/
public class TargetImpl implements ITarget {
public void behavior() {
System.out.println("執行真實的被代理物件的行為,");
}
}
下面的類也是一個被代理的目標物件,但是沒有實作ITarget介面
/**
* @description: 真實的被代理的目標物件
* @author: lmc
* @create: 2019-06-12 15:41
**/
public class Target{
public void behavior() {
System.out.println("執行被代理物件target的行為,");
}
}
/**
* @description: 靜態代理類
* @author: lmc
* @create: 2019-06-12 15:45
**/
public class StaticProxy {
/**
* 持有被代理物件的參考
*/
private ITarget targetImpl;
/**
* 持有被代理物件的參考
*/
private Target target;//一個沒有實作介面的類
/**
* 構造方法初始化值
* @param targetImpl
*/
public StaticProxy(ITarget targetImpl,Target target){
this.targetImpl=targetImpl;
this.target=target;
}
/**
* @description: 被代理之前的增強行為
* @date 2019/6/12 15:56
* @author lmc
*/
private void beforeBehavior(){
System.out.println("執行代理之前需要做的一些事情,");
}
/**
* @description: 被代理之后的增強行為
* @date 2019/6/12 15:57
* @author lmc
*/
private void afterBehavior(){
System.out.println("執行代理之后需要做的一些事情,");
}
/**
* @description: 開始執行代理
* @date 2019/6/12 15:59
* @author lmc
*/
public void startProxy(){
beforeBehavior();
targetImpl.behavior();
target.behavior();
afterBehavior();
}
}
/**
* @description: 靜態代理客戶端 懶漢式單例
* @author: lmc
* @create: 2019-06-12 16:01
**/
public class StaticProxyClient implements Serializable {
private final static StaticProxyClient staticProxyClient=new StaticProxyClient();
private StaticProxyClient(){
if(null != staticProxyClient){
throw new RuntimeException("單例類,不允許被反射實體化");
}
};
public static StaticProxyClient getInstance(){
return staticProxyClient;
}
/**
* @description: 開始靜態代理
* @date 2019/6/12 16:20
* @author lmc
*/
public void startStaticProxy(){
ITarget targetImpl=new TargetImpl();
Target target=new Target();
StaticProxy staticProxy=new StaticProxy(targetImpl,target);
staticProxy.startProxy();
}
/**
* @description: 重寫readResolve,防止序列化破壞單例
* @return java.lang.Object
* @date 2019/6/12 16:18
* @author lmc
*/
private Object readResolve(){
return staticProxyClient;
}
}
/**
* @description: 靜態代理測驗
* @author: lmc
* @create: 2019-06-12 16:10
**/
public class StaticProxyTest {
public static void main(String[] args) {
StaticProxyClient.getInstance().startStaticProxy();
}
}
靜態代理測驗結果

只要代理物件持有被代理物件的參考就可以實作靜態代理了,
JDK-proxy動態代理
/**
* @description: 真實的被代理的目標物件
* @author: lmc
* @create: 2019-06-12 15:41
**/
public class TargetImpl1 implements ITarget {
public void behavior() {
System.out.println("執行被代理物件1的行為,");
}
}
/**
* @description: 真實的被代理的目標物件
* @author: lmc
* @create: 2019-06-12 15:41
**/
public class TargetImpl2 implements ITarget {
public void behavior() {
System.out.println("執行被代理物件2的行為,");
}
}
/**
* @description: 真實的被代理的目標物件
* @author: lmc
* @create: 2019-06-12 15:41
**/
public class TargetImpl3 implements ITarget {
public void behavior() {
System.out.println("執行被代理物件3的行為,");
}
}
/**
* @description: JDK動態代理物件
* @author: lmc
* @create: 2019-06-12 17:00
**/
public class JdkProxy implements InvocationHandler {
/**
* 被代理物件的參考
*/
private ITarget target;
/**
* @description: 獲取代理之后的實體物件
* @param target 被代理物件
* @return com.lmc.gp12380.pattern.proxy.ITarget
* @date 2019/6/12 19:55
* @author lmc
*/
public ITarget getProxyInstance(ITarget target){
this.target = target;
Class<?> clazz = target.getClass();
return (ITarget) Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
beforeBehavior();
//通過反編譯工具可以查看源代碼
byte [] bytes = ProxyGenerator.generateProxyClass("$Proxy0",new Class[]{ITarget.class});
FileOutputStream os = new FileOutputStream("E://$Proxy0.class"); os.write(bytes); os.close();
Object obj = method.invoke(this.target,args);
afterBehavior();
return obj;
}
/**
* @description: 被代理之前的增強行為
* @date 2019/6/12 15:56
* @author lmc
*/
private void beforeBehavior(){
System.out.println("執行代理之前需要做的一些事情,");
}
/**
* @description: 被代理之后的增強行為
* @date 2019/6/12 15:57
* @author lmc
*/
private void afterBehavior(){
System.out.println("執行代理之后需要做的一些事情,");
}
}
下面這個類是測驗沒有實作ITarget介面的代理
/**
* @description: JDK動態代理物件
* @author: lmc
* @create: 2019-06-12 17:00
**/
public class JdkProxy1 implements InvocationHandler {
/**
* 被代理物件的參考
*/
private Target target;
/**
* @description: 獲取代理之后的實體物件
* @param target 被代理物件
* @return com.lmc.gp12380.pattern.proxy.ITarget
* @date 2019/6/12 19:55
* @author lmc
*/
public Target getProxyInstance(Target target){
this.target = target;
Class<?> clazz = target.getClass();
return (Target) Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
beforeBehavior();
//通過反編譯工具可以查看源代碼
byte [] bytes = ProxyGenerator.generateProxyClass("$Proxy0",new Class[]{ITarget.class});
FileOutputStream os = new FileOutputStream("E://$Proxy0.class"); os.write(bytes); os.close();
Object obj = method.invoke(this.target,args);
afterBehavior();
return obj;
}
/**
* @description: 被代理之前的增強行為
* @date 2019/6/12 15:56
* @author lmc
*/
private void beforeBehavior(){
System.out.println("執行代理之前需要做的一些事情,");
}
/**
* @description: 被代理之后的增強行為
* @date 2019/6/12 15:57
* @author lmc
*/
private void afterBehavior(){
System.out.println("執行代理之后需要做的一些事情,");
}
}
下面這個類是Jdk-proxy代理生成的class位元組碼反編譯之后的java類
public final class $Proxy0 extends Proxy
implements ITarget {
public $Proxy0(InvocationHandler invocationhandler) {
super(invocationhandler);
}
public final boolean equals(Object obj) {
try {
return ((Boolean) super.h.invoke(this, m1, new Object[]{
obj
})).booleanValue();
} catch (Error _ex) {
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
return false;
}
public final String toString() {
try {
return (String) super.h.invoke(this, m2, null);
} catch (Error _ex) {
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
return null;
}
public final void behavior() {
try {
super.h.invoke(this, m3, null);
return;
} catch (Error _ex) {
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public final int hashCode() {
try {
return ((Integer) super.h.invoke(this, m0, null)).intValue();
} catch (Error _ex) {
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
return 0;
}
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{
Class.forName("java.lang.Object")
});
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m3 = Class.forName("com.lmc.gp12380.pattern.proxy.ITarget").getMethod("behavior", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
} catch (NoSuchMethodException nosuchmethodexception) {
throw new NoSuchMethodError(nosuchmethodexception.getMessage());
} catch (ClassNotFoundException classnotfoundexception) {
throw new NoClassDefFoundError(classnotfoundexception.getMessage());
}
}
}
這里我們知道了這個反編譯生成的java類實作了ITarget介面
/**
* @description: JDK代理實作測驗
* @author: lmc
* @create: 2019-06-12 19:49
**/
public class JdkProxyTest {
public static void main(String[] args) {
ITarget target1 = (ITarget) new JdkProxy().getProxyInstance(new TargetImpl1());
target1.behavior();
ITarget target2 = (ITarget) new JdkProxy().getProxyInstance(new TargetImpl2());
target2.behavior();
ITarget target3 = (ITarget) new JdkProxy().getProxyInstance(new TargetImpl3());
target3.behavior();
/**
* Target 類沒有實作介面 無法被jdkproxy代理
*/
ITarget target4 = (ITarget) new JdkProxy1().getProxyInstance(new Target());
target4.behavior();
}
}
JDK-proxy動態代理測驗結果

嘿嘿,可以看到target4代理出錯啦,原因就是Target類沒有實作ITarget介面,
CGLib動態代理
/**
* @description: CGLib動態代理物件
* @author: lmc
* @create: 2019-06-12 17:00
**/
public class CGLibProxy implements MethodInterceptor {
/**
* @description: 被代理之前的增強行為
* @date 2019/6/12 15:56
* @author lmc
*/
private void beforeBehavior(){
System.out.println("執行代理之前需要做的一些事情,");
}
/**
* @description: 被代理之后的增強行為
* @date 2019/6/12 15:57
* @author lmc
*/
private void afterBehavior(){
System.out.println("執行代理之后需要做的一些事情,");
}
/**
* @description: 獲取代理之后的實體物件
* @param target 被代理物件
* @return com.lmc.gp12380.pattern.proxy.ITarget
* @date 2019/6/12 19:55
* @author lmc
*/
public Object getProxyInstance(Object target){
Class<?> clazz = target.getClass();
//相當于Proxy,代理的工具類
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
beforeBehavior();
/**
* 利用 cglib 的代理類可以將記憶體中的 class 檔案寫入本地磁盤
*/
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "E://cglib_proxy_class/");
Object obj = methodProxy.invokeSuper(o,objects);
afterBehavior();
return obj;
}
}
/**
* @description: CGLib代理實作測驗
* @author: lmc
* @create: 2019-06-12 20:26
**/
public class CGLibProxyTest {
public static void main(String[] args) {
ITarget target1= (ITarget) new CGLibProxy().getProxyInstance(new TargetImpl1());
target1.behavior();
ITarget target2= (ITarget) new CGLibProxy().getProxyInstance(new TargetImpl2());
target2.behavior();
ITarget target3= (ITarget) new CGLibProxy().getProxyInstance(new TargetImpl3());
target3.behavior();
Target target4= (Target) new CGLibProxy().getProxyInstance(new Target());
target4.behavior();
}
}
CGLib動態代理測驗結果

嘿嘿,可以看到Target沒有實作ITarget介面,也能實作動態代理,
代碼里面是可以生成CGLib動態代理的位元組碼檔案的,也可以反編譯過來看看,因為我是看不懂,我就不展示了,
代理模式的優缺點
優點
- 代理模式能將代理物件與真實被呼叫的目標物件分離,
- 一定程度上降低了系統的耦合度,擴展性好,
- 可以起到保護目標物件的作用,
- 可以對目標物件的功能增強,
缺點
- 代理模式會造成系統設計中類的數量增加,
- 在客戶端和目標物件增加一個代理物件,會造成請求處理速度變慢,
- 增加了系統的復雜度,
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/26054.html
標籤:設計模式
上一篇:JavaScript-代理模式
下一篇:JavaScript-外觀模式
