最近在寫Java服務器,一邊學一邊做,所以很多基礎的不是很清楚,遇到了攔截器都說本質是動態代理。所以好好的看了下。
java的代理用我的理解就是:爲了在接口的方法執行之前或之後添加一些相關操作所以繞個圈用反射獲取方法再執行,這麼做更多的是考慮代碼的可拓展性和解耦合。
jdk的動態代理是基於實現接口完成的,所以我們首先要定義接口,這裏我們定義兩個接口:
public interface DrinkService {
/**
* 普通的接口,用於代理
* */
public String drinkFood(String food);
}
和
public interface EatService {
/**
* 接口用於動態代理測試
* */
public String eatFood(String food);
}
這個兩個接口中分別有一個方法,然後要有一個類來實現這些接口,在實際的業務中這個類創建的對象就是用來處理實際業務的,也是被代理對象。我們會採用反射來在代理中調用這個對象的方法,注意這個方法是實現自接口的方法。
/**
* 被代理的類,jdk的動態代理只能代理接口
* */
public class DrinkAndEat implements EatService,DrinkService{
/**
* 實現接口EatService
* */
@Override
public String eatFood(String food) {
System.out.println("吃"+food);
return "吃"+food;
}
/**
* 實現接口DrinkService
* */
@Override
public String drinkFood(String food) {
System.out.println("喝"+food);
return "喝"+food;
}
}
好了到了這裏我的前置準備已經完成了,正常的業務也都是這麼做的,現在我們需要給這個類設置也給動態代理,所以我們要定義一個類,這個類實現InvocationHandler接口,並且和要被代理的類綁定,如果綁定很簡單:在這個類中寫個屬性,屬性類型就是這個類。
在這個類中要實現接口的方法:
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable
在這個方法中我們會通過反射機制調用被代理類的方法,同樣的java 服務器中的攔截也是在這個方法中實現的,看具體代碼:
/**
* 觸發器,動態代理是通過這個觸發器調用具體的業務實現方法的
* */
public class InvocationHandlerImpl implements InvocationHandler{
/**
* 被代理的對象,即實現具體業務的對象
* */
private DrinkAndEat de;
public void setDe(DrinkAndEat de) {
this.de = de;
}
/**
* 代理通過這個方法調用具體的業務實現
* */
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{
//可在此進行方法調用之前的攔截
System.out.println("方法執行之前攔截");
Object test = method.invoke(de,args[0]);
System.out.println("方法執行之後攔截");
return test;
}
}
要注意的是:Object test = method.invoke(de,args[0]);
中invoke方法的返回值,就是被代理類中方法的返回值,在Object invoke(Object proxy, Method method, Object[] args) throws Throwable
這個方法中我們要將前面的返回值test返回,只有這樣我們纔可以在外面代理中獲取被代理類的方法的返回值的。
下面我們就進行測試:
首先我們要創建代理:
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
在這個方法中我們需要三個參數,一個是被代理類的加載器這樣獲取:
DrinkAndEat de = new DrinkAndEat();
ClassLoader loader = de.getClass().getClassLoader();
一個是被代理類中的接口方法:
Class[] interfaces = de.getClass().getInterfaces();
最後一個參數就是我們剛剛創建的實現了InvocationHandler接口的類:
InvocationHandlerImpl handler = new InvocationHandlerImpl();
handler.setDe(de);
然後我們創建代理:
Object test = Proxy.newProxyInstance(loader, interfaces, handler);
代理創建出來後要進行類型轉換,我們將這個代理類型強轉成接口類型,再調用方法:
DrinkService drink = (DrinkService) test;
EatService eat = (EatService)test;
String drinkString = drink.drinkFood("水");
String eatString = eat.eatFood("米飯");
這是測試的完整代碼:
DrinkAndEat de = new DrinkAndEat();
ClassLoader loader = de.getClass().getClassLoader();
Class[] interfaces = de.getClass().getInterfaces();
InvocationHandlerImpl handler = new InvocationHandlerImpl();
handler.setDe(de);
Object test = Proxy.newProxyInstance(loader, interfaces, handler);
DrinkService drink = (DrinkService) test;
EatService eat = (EatService)test;
String drinkString = drink.drinkFood("水");
String eatString = eat.eatFood("米飯");