歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

Java中的代理模式

代理模式

在某些情況下,一個客戶不想或者不能直接引用一個對象,此時可以通過一個稱之為“代理”的第三者來實現間接引用。代理對象可以在客戶端和目標對象之間起到 中介的作用,並且可以通過代理對象去掉客戶不能看到 的內容和服務或者添加客戶需要的額外服務。

簡單來說代理模式就是通過一個代理對象去訪問一個實際對象,並且可以像裝飾模式一樣給對象添加一些功能。

靜態代理

所謂靜態代理即在程序運行前代理類就已經存在,也就是說我們編寫代碼的時候就已經把代理類的代碼寫好了,而動態代理則是在程序運行時自動生成代理類。

描述起來太過抽象,看一下代碼就明白是怎麼回事了

  • main
public class Main {

    publicstaticvoidmain(String[] args) {
        Water water = new Water();
        WaterProxy waterProxy = new WaterProxy(water);
        waterProxy.drink();
    }

}
  • 接口
//代理類與被代理類共同實現的接口
public interface Drink {
    voiddrink();
}
  • 被代理類
//被代理的類
public class Water implements Drink {

    @Override
    publicvoiddrink() {
        System.out.println("drink water");
    }

}
  • 代理類
//代理類
//與被代理類實現同一個接口
public class DrinkProxy implements Drink {
    
    private Drink drinkImpl;
    
    //通過構造函數傳入Water對象
    publicDrinkProxy(Drink drinkImpl) {
        this.drinkImpl = drinkImpl;
    }
    
    @Override
    publicvoiddrink() {
        //在執行被代理對象的方法前做一些事情
        System.out.println("before drink");
        //執行被代理對象的方法
        drinkImpl.drink();
        //在執行被代理對象的方法後做一些事
        System.out.println("after drink");
    }

}

執行結果

before drink
drink water
after drink

動態代理

有時候我們只想改變代理類所代理的類,但是代理對象執行實際對象的方法前後所做的事情是一樣的,正所謂鐵打的代理類,流水的被代理類。而采用靜態代理就只能代理實現了同一接口的類,如果要代理任意類則必須寫很多重復的代理類。此時我們可以采用動態代理,java已經為實現動態代理提供了一套比較方便的工具。

  • java.lang.reflect.Proxy類中可以動態生成代理對象的方法
  /**     *返回實現了指定接口的對象,調用代理對象的方法會調用      *InvocationHandler的invoke方法     *     * @param loader 獲取代理類所使用的類加載器     * @param interfaces 代理類所要實現的接口     * @param h 實現了InvocationHandler接口的對象     * @return  代理對象     */
publicstatic Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces, InvocationHandler h)
  • InvocationHandler接口
/** *每個代理類都有一個關聯的InvocationHandler *當代理對象執行一個方法的時候會直接執行invoke方法 */
public interface InvocationHandler {

    /**     * @param 調用該方法的代理對象     * @param method 代理對象所調用的方法     * @param args 調用的方法的參數     * @return  調用的方法的返回值     */
    public Object invoke(Object proxy, Method method, Object[] args)
}

描述總是比較抽象,還是看實際例子比較好理解

例子

  • InvocationHandler接口的實現類
public class CommonInvocationHandler implements InvocationHandler {
    
    //被代理的對象
    private Object proxied;
    
    publicCommonInvocationHandler(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

public class Main {

    publicstaticvoidmain(String[] args) {
        //被代理的對象
        Water water = new Water();
        //動態獲取代理對象
        Drink waterProxy = 
                (Drink) Proxy.newProxyInstance(water.getClass().getClassLoader(),
                        water.getClass().getInterfaces(), 
                        new CommonInvocationHandler(water));
        //通過代理對象調用方法
        waterProxy.drink();
    }

}
  • 輸出結果
before doing something
drink water
after doing something

也可以不要具體的被代理對象,但是必須有相應的接口(沒有實現接口的類可以使用cglib實現動態代理)才可以動態獲取代理對象。像最近比較火的Retrofit就直接通過聲明好的接口使用動態代理進行網絡請求。

例子

簡單的模擬一下retrofit

  • POST注解
//Post請求注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface POST {
  String value() default"";
}
  • Query注解
//Post請求注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface POST {
  String value() default"";
}
  • Service接口
public interface Service {
    //用POST注解聲明請求的方式和相對路徑
    @POST("/login")
    //@Query注解聲明請求的參數名
    voidlogin(@Query("username")String username, 
            @Query("password")String password);
}
  • Main
public class Main {

    publicstaticvoidmain(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");
    }

}

Copyright © Linux教程網 All Rights Reserved