1、靜態代理
靜態代理在使用時,需要定義接口或者父類,被代理對象與代理對象一起實現相同的接口或者是繼承相同父類.
在編譯時就已經將接口,被代理類,代理類等確定下來。在程序運行之前,代理類的.class文件就已經生成。
缺點:一旦接口增加方法,目標對象與代理對象都要維護。
1.1、類圖結構
1.2、代碼實現
Subject.java
package com.design.demo.proxy;
/**
* 抽象接口
* @author administrator
* @date 2020-05-24 23:47
*/
public interface Subject {
/**
* 被代理方法的具體實現
*/
public void doSomething();
}
SubjectImpl.java
package com.design.demo.proxy;
/**
* 抽象接口的實現
* 真實的實現者
*
* @author administrator
* @date 2020-05-24 23:49
*/
public class SubjectImpl implements Subject {
@Override
public void doSomething() {
System.out.println("doSomething...");
}
}
SubjectProxy.java
package com.design.demo.proxy;
/**
* 抽象接口的具體實現
* 代理類,持有真實實現的實例
*
* @author administrator
* @date 2020-05-24 23:49
*/
public class SubjectProxy implements Subject {
private Subject subject;
public SubjectProxy(Subject subject) {
this.subject = subject;
}
@Override
public void doSomething() {
System.out.println("before doSomething...");
subject.doSomething();
System.out.println("after doSomething...");
}
}
ClientTest.java
package com.design.demo.proxy;
/**
* 測試客戶類
*
* @author administrator
* @date 2020-05-25 0:04
*/
public class ClientTest {
public static void main(String[] args) {
Subject targetSubject = new SubjectImpl();
Subject proxySubject = new SubjectProxy(targetSubject);
proxySubject.doSomething();
}
}
測試類運行結果
2、動態代理
代理類在程序運行時創建的代理方式被成爲動態代理
2.1、JDK動態代理
缺點:目標必須實現接口,不然無法實現動態代理
2.1.1、類圖結構
2.1.2、代碼實現
Subject.java
package com.design.demo.proxy1;
/**
* 抽象接口
* @author administrator
* @date 2020-05-24 23:47
*/
public interface Subject {
/**
* 被代理方法的具體實現
*/
public void doSomething();
}
SubjectImpl.java
package com.design.demo.proxy1;
/**
* 抽象接口的實現
* 真實的實現者
*
* @author administrator
* @date 2020-05-24 23:49
*/
public class SubjectImpl implements Subject {
@Override
public void doSomething() {
System.out.println("doSomething...");
}
}
InvocationHandlerImpl.java
package com.design.demo.proxy1;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* 調用處理器實現類
* 每次生成動態代理類對象時都需要指定一個實現了該接口的調用處理器對象
*
* @author administrator
* @date 2020-05-25 22:53
*/
public class InvocationHandlerImpl implements InvocationHandler {
/**
* 這個就是我們要代理的真實對象
*/
private Object target;
/**
* 構造方法,給我們要代理的真實對象賦初值
*
* @param target
*/
public InvocationHandlerImpl(Object target) {
this.target = target;
}
/**
* 該方法負責集中處理動態代理類上的所有方法調用。
* 調用處理器根據這三個參數進行預處理或分派到委託類實例上反射執行
*
* @param proxy 代理類實例
* @param method 被調用的方法對象
* @param args 調用參數
* @return
* @throws Throwable
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//在代理真實對象前我們可以添加一些自己的操作
System.out.println("before doSomething...");
System.out.println("Method:" + method);
//當代理對象調用真實對象的方法時,其會自動的跳轉到代理對象關聯的handler對象的invoke方法來進行調用
Object returnValue = method.invoke(target, args);
//在代理真實對象後我們也可以添加一些自己的操作
System.out.println("after doSomething...");
return returnValue;
}
}
ClientTest.java
package com.design.demo.proxy1;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
/**
* 測試客戶類
*
* @author administrator
* @date 2020-05-25 23:01
*/
public class ClientTest {
public static void main(String[] args) {
//代理的真實對象
Subject realSubject = new SubjectImpl();
/**
* InvocationHandlerImpl 實現了 InvocationHandler 接口,並能實現方法調用從代理類到委託類的分派轉發
* 其內部通常包含指向委託類實例的引用,用於真正執行分派轉發過來的方法調用.
* 即:要代理哪個真實對象,就將該對象傳進去,最後是通過該真實對象來調用其方法
*/
InvocationHandler handler = new InvocationHandlerImpl(realSubject);
ClassLoader loader = realSubject.getClass().getClassLoader();
Class[] interfaces = realSubject.getClass().getInterfaces();
//該方法用於爲指定類裝載器、一組接口及調用處理器生成動態代理類實例
Subject subject = (Subject) Proxy.newProxyInstance(loader, interfaces, handler);
subject.doSomething();
}
}
測試類運行結果
2.2、CGLIB動態代理
上面的靜態代理和JDK動態代理模式都是要求目標對象是實現一個接口的目標對象,但是有時候目標對象只是一個單獨的對象,並沒有實現任何的接口,這個時候就可以使用以目標對象子類的方式類實現代理,這種方法就叫做:Cglib代理
優點:實現了不適用接口就可以實現動態代理
缺點:也是因爲它的優點導致,實現類沒有統一的限定格式
2.2.1、類圖結構
2.2.2、代碼實現
SubjectImpl.java
package com.design.demo.proxy2;
/**
* 真實的實現者,沒有實現任何接口
*
* @author administrator
* @date 2020-05-24 23:49
*/
public class SubjectImpl {
public void doSomething() {
System.out.println("doSomething...");
}
}
MyMethodInterceptor.java
package com.design.demo.proxy2;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* 方法攔截器
*
* @author administrator
* @date 2020-05-25 23:52
*/
public class MyMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
//在代理真實對象前我們可以添加一些自己的操作
System.out.println("before doSomething...");
//注意這裏的方法調用,不是用反射
Object object = methodProxy.invokeSuper(obj, args);
//在代理真實對象後我們也可以添加一些自己的操作
System.out.println("after doSomething...");
return object;
}
}
ClientTest.java
package com.design.demo.proxy2;
import com.design.demo.proxy1.SubjectImpl;
import net.sf.cglib.core.DebuggingClassWriter;
import net.sf.cglib.proxy.Enhancer;
/**
* 測試客戶類
*
* @author administrator
* @date 2020-05-27 20:41
*/
public class ClientTest {
public static void main(String[] args) {
//在指定目錄下生成動態代理類,有興趣可以反編譯看一下里面到底是一些什麼東西
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "E:\\1");
//創建Enhancer對象,類似於JDK動態代理的Proxy類,下一步就是設置幾個參數
Enhancer enhancer = new Enhancer();
//設置目標類的字節碼文件
enhancer.setSuperclass(SubjectImpl.class);
//設置回調函數
enhancer.setCallback(new MyMethodInterceptor());
//這裏的creat方法就是正式創建代理類
SubjectImpl subjectProxy = (SubjectImpl) enhancer.create();
//調用代理類的方法
subjectProxy.doSomething();
}
}
測試類運行結果