設計模式(4)代理模式
談及設計模式裡面的代理模式,不得不說,這個模式在網上有很多的應用場景。例如說,Spring裡面的代理機制,dubbo的動態代理模式生成proxy過程等等。
代理模式的概念:
代理模式是指使用代理物件來執行某一個物件所要執行的相應方法。
我們常說的代理模式主要劃分為了兩種型別:
靜態代理
所謂的靜態代理我的認識就是一種靈活性較差的代理設計。其基本的設計結構圖如下所示:
每次做代理的時候,都需要實現原來的介面方法,然後在建構函式中將相應的代理物件給注入進去。這樣子做起來顯得有點繁瑣。如果說我們不去實現它相應的介面的話,那麼這樣所帶來的效果就不是叫做代理了。(因為只能實現介面中的部分功能,並不夠完善)。
例如說以下案例程式碼:
/**
* 作者:idea
* 日期:2018/5/30
* 描述:
*/
public interface Subject {
public void request();
}
/** * 作者:idea * 日期:2018/5/30 * 描述:代理物件 */ public class ProxySubject implements Subject{ public Subject subject; public ProxySubject(){ this.subject=new RealSubject(); } public void before(){ System.out.println("this is before"); } public void after(){ System.out.println("this is after"); } @Override public void request() { this.before(); this.subject.request(); this.after(); } }
/**
* 作者:idea
* 日期:2018/5/30
* 描述:
*/
public class RealSubject implements Subject{
@Override
public void request() {
System.out.println("請求-------");
}
}
/** * 作者:idea * 日期:2018/5/30 * 描述: */ public class Test { public static void main(String[] args) { Subject subject=new ProxySubject(); subject.request(); //使用了代理物件之後,相應的請求會被橫向攔截 } }
動態代理
關於動態代理部分而言,我們常說的幾種型別分別就是cglib代理,還有jdk代理了。
舉個簡單的jdk代理模式程式碼案例:
/**
* @author idea
* @data 2018/11/11
*/
public interface Decode {
String decode(String content);
}
/**
* @author idea
* @data 2018/11/11
*/
public class DecodeImpl implements Decode{
@Override
public String decode(String content){
return content+"decode";
}
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 代理工廠
*
* @author idea
* @data 2018/11/11
*/
public class InvocationFactory implements InvocationHandler {
private Object target;
public InvocationFactory(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理執行");
Object value = method.invoke(target, args);
return value;
}
public static void main(String[] args) {
InvocationHandler invocationHandler = new InvocationFactory(new DecodeImpl());
Decode decode = (Decode) Proxy.newProxyInstance(DecodeImpl.class.getClassLoader(), DecodeImpl.class.getInterfaces(), invocationHandler);
System.out.println(decode.decode("asd"));
}
}
通過上述的一個簡單案例可以先大致的瞭解什麼是jdk代理,以及jdk代理的基本實現步驟。使用jdk來進行代理的時候,要求被代理物件有相應的介面和介面實現類。在代理Proxy建立的時候需要獲取到相應目標物件的類載入器,以及介面,還有InvocationHandler的例項。在InvocationHandler裡面,對於invoke部分可以引用相應的物件控制代碼,然後進行方法的擴充套件實現,從而實現代理的效果。
cglib的代理機制程式碼案例:
/**
* @author idea
* @data 2018/11/11
*/
public class Car {
public void test(){
System.out.println("this is test");
}
}
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* @author idea
* @data 2018/11/11
*/
public class CarCglib implements MethodInterceptor {
private Object target;
public Object getTarget(Object target) {
this.target = target;
Enhancer enhancer = new Enhancer();
//設定它的代理物件
enhancer.setSuperclass(this.target.getClass());
//回撥物件
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("cglib執行代理");
Object value = methodProxy.invokeSuper(o, objects);
return value;
}
public static void main(String[] args) {
Car car=new Car();
CarCglib carCglib=new CarCglib();
Car car2= (Car) carCglib.getTarget(car);
car2.test();
}
}
對於cglib而言,在執行代理的時候,可以動態地調整相應的返回值:
public Object getTarget2(Object target) {
this.target = target;
Enhancer enhancer = new Enhancer();
//設定它的代理物件
enhancer.setSuperclass(this.target.getClass());
//回撥物件
enhancer.setCallback(new FixedValue() {
@Override
public Object loadObject() throws Exception {
return "使用fixedvalue可以替換掉方法的返回值";
}
});
return enhancer.create();
}
public Object getTarget3(Object target) {
this.target = target;
Enhancer enhancer = new Enhancer();
//設定它的代理物件
enhancer.setSuperclass(this.target.getClass());
//回撥物件
enhancer.setCallback(new InvocationHandler() {
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
if (method.getDeclaringClass() != Object.class && method.getReturnType() == String.class) {
return "Hello cglib!";
} else {
return method.invoke(o, objects);
}
}
});
return enhancer.create();
}
public Object getTarget4(Object target) {
this.target = target;
Enhancer enhancer = new Enhancer();
//設定它的代理物件
enhancer.setSuperclass(this.target.getClass());
//回撥物件
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
if(method.getDeclaringClass() != Object.class && method.getReturnType() == String.class) {
return "Hello cglib!";
} else {
return methodProxy.invokeSuper(o, objects);
}
}
});
return enhancer.create();
}
這裡面儘量少用InvocationHandler來充當回撥物件,這樣容易產生死迴圈現象的發生。
來看看下邊的一個比較圖:
cglib動態代理 | jdk動態代理 |
---|---|
cglib主要是通過和目標物件使用相同的一個父類,然後來實現相應的代理機制。這種機制並不要求目標物件要實現介面 | Jdk的動態代理,是使用反射技術獲得類的載入器並且建立例項,根據類執行的方法在執行方法的前後傳送通知。 |
代理模式的應用場景也是蠻多的,例如說Spring裡面的AOP,Dubbo裡面的RPC呼叫,都有使用到代理模式的這種思想,因此感覺這塊的知識的也是蠻重要的。