1. 程式人生 > 其它 >Java 代理學習筆記

Java 代理學習筆記

java代理

特徵: 代理類和委託類有同樣的介面, 代理類主要為委託類預處理訊息, 過濾訊息, 吧訊息轉發給委託類, 以及處理事後訊息, 代理類的物件並不真正實現服務, 而是通過呼叫委託類的相關方法, 來提供特定的服務.

簡單的說就是,我們在訪問實際物件時,是通過代理物件來訪問的,代理模式就是在訪問實際物件時引入一定程度的間接性, 因為這種間接性我們可以附加多種用途.

代理模式

  • 代理模式: 為其他物件為提供一種代理以控制對這個物件的訪問.
classDiagram class Super{ <<interface>> +operation() } class SubObject{ +operation() } Super <|.. SubObject : 實現 class Proxy{ +operation() } Super <|.. Proxy : 實現 SubObject <-- Proxy : 控制權

Super類定義了SubObject和Proxy的公用介面, 這樣在需要使用SubObject的任何地方都可以使用Proxy.

應用場景:

  • 遠端代理: 遠端物件的本地代表, 本地方法呼叫這個物件會被轉發到遠端中.
  • 虛擬代理: 虛擬化開銷大的物件
  • 安全代理: 控制訪問許可權
  • 智慧指引: 呼叫真實的物件時, 處理另外一些事

靜態代理

在編譯時期就已經將介面, 被代理類等確定下來. 在程式執行之前代理類的.class檔案就已經生成.

例子:

classDiagram class Interface{ <<interface>> void doSomthing() void doSomthingElse() } class InterfaceImplFirst{ void doSomthing() void doSomthingElse() } class InterfaceImplTwo{ void doSomthing() void doSomthingElse() } Interface <|.. InterfaceImplFirst : 實現 Interface <|.. InterfaceImplTwo : 實現 InterfaceImplFirst <-- InterfaceImplTwo : 代理

Interface.class

public interface Interface {
    void doSomething();
    void doSomethingElse(String arg);
}

InterfaceImplFirst.class

public class InterfaceImplFirst implements Interface{

    public void doSomething() {
        System.out.println("InterfaceImplFirst: doSomething");
    }

    public void doSomethingElse(String arg) {
        System.out.println("InterfaceImplFirst: doSomethingElse " + arg);
    }
}

InterfaceImplTwo.class

public class InterfaceImplTwo implements Interface {
    private final Interface interfaceImplFirst;

    public InterfaceImplTwo(Interface interfaceImplFirst){
        this.interfaceImplFirst = interfaceImplFirst;
    }

    public void doSomething() {
        interfaceImplFirst.doSomething();
        System.out.println("InterfaceImplTwo: doSomething");
    }

    public void doSomethingElse(String arg) {
        interfaceImplFirst.doSomethingElse(arg);
        System.out.println("doSomethingElse: doSomethingElse " + arg);
    }
}

反射

基本使用:

//獲取Class物件的三種方法:
Class<User> clazz = User.class;
Class<User> clazz = user.getClass();
Class<User> clazz = Class.forName(User.class);
//Class獲取屬性
public Field getField(String name) {...} //獲取指定名稱欄位
public Method getMethod(String name, Class<?>... parameterTypes) {...} //獲取指定名稱和簽名的方法
public Constructor<T> getConstructor(Class<?>... parameterTypes){...} //獲取指定引數列表公有構造器
public Constructor<?>[] getConstructors() throws SecurityException {...} //獲取所有公有構造器
//記載和類位於同一目錄下的資源, 通過類載入器獲取
public java.net.URL getResource(String name) {...} //返回URL形式的資源
public InputStream getResourceAsStream(String name) {...} //返回輸入流形式的資源
//Constructor構造器的操作
public void setAccessible(boolean flag) {...} //給予私有化構造方法,成員方法,成員變數操作許可權
public T newInstance(Object ... initargs) {...} //通過這個構造器宣告一個類的新例項
public int getModifiers() {...} //獲取構造器的訪問修飾符
//Method方法呼叫
public Object invoke(Object obj, Object... args) {...} //obj為操作物件靜態方法為null, args為方法引數

詳情可以參考部落格

例子:

Class<?> clazz = Class.forName("java.util.Date");
Date instance = (Date)clazz.newInstance();

使用反射覆制陣列:

使用Object類實現:

public static Object copy(Object array, int newLength){
    Objects.requireNonNull(array);
    Class<?> clazz = array.getClass();
    System.out.println(clazz.getName());
    if (!clazz.isArray()) {
        return null;
    }
    Class<?> componentType = clazz.getComponentType();
    Object newArray = Array.newInstance(componentType, newLength);
    System.arraycopy(array, 0, newArray, 0, newLength);
    return newArray;
}

泛型方式實現:

public static <T> T copy(T array, int newLength){
    Objects.requireNonNull(array);
    Class<?> clazz = array.getClass();
    if (!clazz.isArray()){
        return null;
    }
    Class<?> componentType = clazz.getComponentType();
    Object o = Array.newInstance(componentType, newLength);
    System.arraycopy(array, 0, o, 0, newLength);
    return (T)o;
}

使用泛型和反射實現模型轉換:

public class Converter {
    public static <T, S> T convert(S s, Class<T> clazz){
        try {
            T t = clazz.newInstance();
            if (s == null){
                return t;
            }
            BeanUtils.copyProperties(s, t);
            return t;
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }
}

動態代理

在程式執行中建立物件, 不需要程式設計師去建立檔案

功能:

  1. 控制訪問, 在代理中控制是否可以呼叫目標的方法
  2. 功能增強, 在完成目標呼叫時, 附加額外的一些功能, 這些額外的功能叫做功能增強

優點:

  1. 不用建立代理類

  2. 可以給不同的目標隨時建立代理

流程:

JDK實現動態代理:

  1. 建立一個處理程式, 經過這個處理程式處理的類的方法執行都會將耗時列印在控制檯上
class MyInvokeHandler<T> implements InvocationHandler {
    private T proxied = null;

    public MyInvokeHandler(T proxied){
        this.proxied = proxied;
    }

    /**
     *
     * @param proxy 
     * @param method 代理物件的動作
     * @param args 動作的引數列表
     * @return 返回
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        long before = System.currentTimeMillis();
        Object invoke = method.invoke(proxied, args); //使用反射呼叫類中的方法
        long after = System.currentTimeMillis();
        System.out.println(method.getName() + " used time: " + (after - before));
        return invoke;
    }
}
  1. 下面是被代理物件和其實現介面
interface Super{
    void printHello(String param);
}

class Proxied implements Super{

    public void printHello(String param){
        System.out.println("Hello Proxied: " + param);
    }
}
  1. 實現動態代理
//1. 建立一個被代理類, 就是被處理程式處理的類
Proxied proxied = new Proxied();

//2. 建立程式處理類處理這個被代理類
InvocationHandler handler = new MyInvokeHandler<>(proxied);

//3. 根據程式處理類動態生成代理類
Class<?> proxyClass = Proxy.getProxyClass(Super.class.getClassLoader(), Super.class);

//4. 生成代理類的構造器
Constructor<?> constructor = proxyClass.getConstructor(InvocationHandler.class);

//5. 使用代理類的構造器生成代理物件
Super s = (Super)constructor.newInstance(handler);

//6. 檢測代理是否有效
s.printHello("axianibiru");

或者:

//1. 建立被代理類
Proxied proxied = new Proxied();
//2. 生成代理處理程式
InvocationHandler handler = new MyInvokeHandler<Super>(proxied);
//3. 根據代理處理程式動態生成代理類
Super s = (Super)Proxy.newProxyInstance(Super.class.getClassLoader(), new Class<?>[]{Super.class}, handler);
//4. 呼叫被代理後的方法
s.printHello("axianibiru");
//5. 檢視代理類行資訊
Class<? extends Super> clazz = s.getClass();
//6. 檢視繼承和實現關係
Class<?> superclass = clazz.getSuperclass();
System.out.print(clazz.getName() + " extends " + superclass);
Class<?>[] interfaces = clazz.getInterfaces();
for (Class<?> anInterface : interfaces) {
    System.out.print( " implements " + anInterface.getName() + " ");
}

輸出:

Hello Proxied: axianibiru
printHello used time: 0
com.proxyTest.dynamicProxy.thridDemo.$Proxy0 extends class java.lang.reflect.Proxy implements com.proxyTest.dynamicProxy.thridDemo.Super