java 代理模式詳解
前言:
在某些情況下,一個客戶不想或者不能直接引用一個對象,此時可以通過一個稱之為“代理”的第三者來實現間接引用。代理對象可以在客戶端和目標對象之間起到 中介的作用,并且可以通過代理對象去掉客戶不能看到 的內容和服務或者添加客戶需要的額外服務。
簡單來說代理模式就是通過一個代理對象去訪問一個實際對象,并且可以像裝飾模式一樣給對象添加一些功能。
靜態代理
所謂靜態代理即在程序運行前代理類就已經存在,也就是說我們編寫代碼的時候就已經把代理類的代碼寫好了,而動態代理則是在程序運行時自動生成代理類。
描述起來太過抽象,看一下代碼就明白是怎么回事了
main
1
2
3
4
5
6
7
8
9
|
public class Main { public static void main(String[] args) { Water water = new Water(); WaterProxy waterProxy = new WaterProxy(water); waterProxy.drink(); } } |
接口
1
2
3
4
|
//代理類與被代理類共同實現的接口 public interface Drink { void drink(); } |
被代理類
1
2
3
4
5
6
7
8
9
|
//被代理的類 public class Water implements Drink { @Override public void drink() { System.out.println( "drink water" ); } } |
代理類
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
//代理類 //與被代理類實現同一個接口 public class DrinkProxy implements Drink { private Drink drinkImpl; //通過構造函數傳入Water對象 public DrinkProxy(Drink drinkImpl) { this .drinkImpl = drinkImpl; } @Override public void drink() { //在執行被代理對象的方法前做一些事情 System.out.println( "before drink" ); //執行被代理對象的方法 drinkImpl.drink(); //在執行被代理對象的方法后做一些事 System.out.println( "after drink" ); } } |
執行結果
1
2
3
|
before drink drink water after drink |
動態代理
有時候我們只想改變代理類所代理的類,但是代理對象執行實際對象的方法前后所做的事情是一樣的,正所謂鐵打的代理類,流水的被代理類。而采用靜態代理就只能代理實現了同一接口的類,如果要代理任意類則必須寫很多重復的代理類。此時我們可以采用動態代理,Java已經為實現動態代理提供了一套比較方便的工具。
java.lang.reflect.Proxy類中可以動態生成代理對象的方法
1
2
3
4
5
6
7
8
9
10
|
/** *返回實現了指定接口的對象,調用代理對象的方法會調用 *InvocationHandler的invoke方法 * * @param loader 獲取代理類所使用的類加載器 * @param interfaces 代理類所要實現的接口 * @param h 實現了InvocationHandler接口的對象 * @return 代理對象 */ public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces, InvocationHandler h) |
InvocationHandler接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
/** *每個代理類都有一個關聯的InvocationHandler *當代理對象執行一個方法的時候會直接執行invoke方法 */ public interface InvocationHandler { /** * @param 調用該方法的代理對象 * @param method 代理對象所調用的方法 * @param args 調用的方法的參數 * @return 調用的方法的返回值 */ public Object invoke(Object proxy, Method method, Object[] args) } |
描述總是比較抽象,還是看實際例子比較好理解
例子
InvocationHandler接口的實現類
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
public class CommonInvocationHandler implements InvocationHandler { //被代理的對象 private Object proxied; public CommonInvocationHandler(Object proxied) { this .proxied = proxied; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //在調用被代理對象的方法前做一些事情 System.out.println( "before doing something" ); //調用被代理對象的方法 Object result = method.invoke(proxied, args); //在調用被代理對象的方法后做一些事情 System.out.println( "after doing something" );; return result; } } |
Main
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
public class Main { public static void main(String[] args) { //被代理的對象 Water water = new Water(); //動態獲取代理對象 Drink waterProxy = (Drink) Proxy.newProxyInstance(water.getClass().getClassLoader(), water.getClass().getInterfaces(), new CommonInvocationHandler(water)); //通過代理對象調用方法 waterProxy.drink(); } } |
輸出結果
1
2
3
|
before doing something drink water after doing something |
也可以不要具體的被代理對象,但是必須有相應的接口(沒有實現接口的類可以使用cglib實現動態代理)才可以動態獲取代理對象。像最近比較火的Retrofit就直接通過聲明好的接口使用動態代理進行網絡請求。
例子
簡單的模擬一下retrofit
POST注解
1
2
3
4
5
6
|
//Post請求注解 @Target (ElementType.METHOD) @Retention (RetentionPolicy.RUNTIME) public @interface POST { String value() default "" ; } |
Query注解
1
2
3
4
5
6
|
//Post請求注解 @Target (ElementType.METHOD) @Retention (RetentionPolicy.RUNTIME) public @interface POST { String value() default "" ; } |
Service接口
1
2
3
4
5
6
7
|
public interface Service { //用POST注解聲明請求的方式和相對路徑 @POST ( "/login" ) //@Query注解聲明請求的參數名 void login( @Query ( "username" )String username, @Query ( "password" )String password); } |
Main
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
public class Main { public static void main(String[] args) { // 動態獲取Service接口的代理 Service service = (Service) Proxy.newProxyInstance(Service. class .getClassLoader(), new Class[] { Service. class }, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 通過注解獲取請求的相對路徑 String retativePath = ((POST) method.getAnnotations()[ 0 ]).value(); System.out.println( "relative path: " + retativePath); // 獲取參數的注解 Annotation[][] parameterAnnotations = method.getParameterAnnotations(); // 通過參數的注解獲取請求參數 for ( int i = 0 ; i < parameterAnnotations.length; i++) { if (parameterAnnotations[i].length != 0 ) { for ( int j = 0 ; j < parameterAnnotations[i].length; j++) { Query query = (Query) parameterAnnotations[i][j]; System.out.println(query.value() + ": " + args[i].toString()); } } } return null ; } }); // 調用代理對象的方法 service.login( "hello" , "world" ); } } |
感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持!