介紹
- 什麼是代理模式
- 靜態代理
- JDK 自帶的動態代理
- CGLIB 動態代理
代理模式
意圖:爲其他對象提供一種代理,以控制對這個對象的訪問。
例子:買火車票不一定要在火車站,去網上各個代理商那裏也可以
代碼思路:實體類 A 實現了接口 IA,而實體類 A 很複雜,那麼使用實體類 B 去實現接口 IA,通過實體類 B 調用實體類 A 去滿足功能。
注意事項:
1、和適配器模式的區別:適配器模式主要改變所考慮對象的接口,而代理模式不能改變所代理類的接口。
2、和裝飾器模式的區別:裝飾器模式爲了增強功能,而代理模式是爲了加以控制。
靜態代理
靜態代理其實就是在程序運行之前,提前寫好被代理方法的代理類,編譯後運行。
下面代理模式實現需要寫很多代碼,不建議使用。
// 接口 A
public interface IServiceA {
void sayHello(String str);
}
// ServiceAImpl 實現接口 A
public class ServiceAImpl implements IServiceA {
@Override
public void sayHello(String str) {
System.out.println(str);
}
}
// 代理對象 ServiceProxyAImpl 也實現接口 A,並且構造函數必須傳入接口 A 的實例
public class ServiceProxyAImpl implements IServiceA {
private IServiceA iServiceA;
public ServiceProxyAImpl(IServiceA iServiceA) {
this.iServiceA = iServiceA;
}
@Override
public void sayHello(String str) {
iServiceA.sayHello(str+ " is proxy ");
}
}
// 進行測試
public class Main {
public static void main(String[] args) {
IServiceA realA = new ServiceAImpl();
// 傳入真實對象
IServiceA proxyA = new ServiceProxyAImpl(realA);
realA.sayHello("realA");
// 代理對象的調用
proxyA.sayHello("proxyA");
}
}
程序輸出 :
ServiceAImpl sayrealA
ServiceAImpl sayproxyA is proxy
動態代理
通過反射機制,在運行時生成代理類
JDK 動態代理
被代理的類需要是接口實現,如果被代理的類沒有實現接口,就不能使用 JDK 動態代理
- java.lang.reflect.Proxy:生成動態代理類和對象;
- java.lang.reflect.InvocationHandler:通過 invoke 方法實現對真實角色的代理訪問。
// 接口 A
public interface IServiceA {
void sayHello(String str);
}
// ServiceAImpl 實現接口 A
public class ServiceAImpl implements IServiceA {
@Override
public void sayHello(String str) {
System.out.println(str);
}
}
public class ProxyHandler implements InvocationHandler {
private IServiceA serviceA;
public ProxyHandler(IServiceA serviceA) {
this.serviceA = serviceA;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(serviceA, args);
}
}
public class MainProxy {
public static void main(String[] args) {
// 真實對象
IServiceA serviceA = new ServiceAImpl();
// 通過 Proxy 得到代理對象
IServiceA proxyServiceA = (IServiceA) Proxy
.newProxyInstance(serviceA.getClass().getClassLoader(), serviceA.getClass().getInterfaces(),
new ProxyHandler(serviceA));
serviceA.sayHello("serviceA");
proxyServiceA.sayHello("proxy serviceA");
}
}
程序輸出:
serviceA
proxy serviceA
CGLIB 動態代理
通過改變字節碼,對需要被代理的類,生成他的子類,子類去覆蓋被代理類的方法,其中 private、final 修飾的方法不會被重寫。
- 首先實現 MethodInterceptor,方法調用會被轉發到該類的 intercept() 方法。
- 通過 Enhancer 來指定要代理的目標對象、實際處理代理邏輯的對象,最終通過調用 create() 方法得到代理對象
// 接口 A
public interface IServiceA {
void sayHello(String str);
}
// ServiceAImpl 實現接口 A
public class ServiceAImpl implements IServiceA {
@Override
public void sayHello(String str) {
System.out.println(str);
}
}
public class MyMethodInterceptor implements MethodInterceptor {
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
return methodProxy.invokeSuper(o,objects);
}
}
public class CGLIBMain {
public static void main(String[] args) {
IServiceA serviceA = new ServiceAImpl();
serviceA.sayHello("serviceA");
Enhancer enhancer = new Enhancer(); // 字節碼增強
enhancer.setSuperclass(ServiceAImpl.class); // 設置父類
enhancer.setCallback(new MyMethodInterceptor()); // 設置回調方法
IServiceA proxyA = (IServiceA) enhancer.create();
proxyA.sayHello("proxyA");
}
}
程序輸出:
serviceA
proxyA