天天看點

面試官:說說Java中有哪些代理?

面試官:說說Java中有哪些代理?

代理類相當于是原本的類的class對象+自定義操作,了解為一層封裝。代理類主要負責為委托類預處理消息、過濾消息、把消息轉發給委托類,以及事後處理消息等。代理類的對象本身并不真正實作服務,而是通過調用委托類的對象的相關方法,來提供特定的服務。簡單的說就是,我們在通路實際對象時,是通過代理對象來通路的,代理模式就是在通路實際對象時引入一定程度的間接性,因為這種間接性,可以附加多種用途。

假如一個班的同學要向老師交班費,但是都是通過班長把自己的錢轉交給老師。這裡,班長就是代理學生上交班費,班長就是學生的代理。

靜态代理

由程式員建立或特定工具自動生成源代碼,也就是在編譯時就已經将接口,被代理類,代理類等确定下來。在程式運作之前,代理類的.class檔案就已經生成。

流程:

  1. 定義一個接口,定義了一個抽象方法。
  2. 被代理類實作了接口,重寫了方法。
  3. 代理類也實作接口,然後将被代理類引用,并通過構造方法引入,然後重寫方法,此時可以調用被代理類的方法
  4. main方法new了被代理類和代理類兩個對象,前者作為參數傳給後者,并調用後者的方法。

缺點:每個被代理類都要寫一個針對的代理類。

動态代理

代理類在程式運作時建立的代理方式被成為動态代理。動态代理的優勢在于可以很友善的對代理類的函數進行統一的處理,而不用修改每個代理類中的方法

若目标對象實作了接口,spring預設使用「JDK的動态代理」。

  • 優點:因為有接口,是以使系統更加松耦合;
  • 缺點:為每一個目标類建立接口;

若目标對象沒有實作任何接口,spring使用「CGLib動态代理」。

  • 優點:因為代理類與目标類是繼承關系,是以不需要有接口的存在。
  • 缺點:因為沒有使用接口,是以系統的耦合性沒有使用JDK的動态代理好。

「1、jdk動态代理」

newProxyInstance()會傳回一個實作了指定接口的代理對象,對該對象的所有方法調用都會轉發給InvocationHandler.invoke()方法。了解上述代碼需要對Java反射機制有一定了解。動态代理神奇的地方就是:

  1. 代理對象是在程式運作時産生的,而不是編譯期;
  2. 對代理對象的所有接口方法調用都會轉發到InvocationHandler.invoke()方法,在invoke()方法裡我們可以加入任何邏輯,比如修改方法參數,加入日志功能、安全檢查功能等;之後我們通過某種方式執行真正的方法體,可以通過反射調用對象的相應方法,還可以通過RPC調用遠端方法。

使用方法:代理類實作「InvocationHandler」,然後引用被代理類,并在構造方法裡傳入。然後重寫invoke方法,為具體操作代碼。然後通過

Proxy.newProxyInstance(  getClass().getClassLoader(), new Class<?>[] {Hello.class}, // 2. 代理需要實作的接口,可以有多個  
new LogInvocationHandler(new HelloImp()));// 3. 方法調用的實際處理者

      

Java動态代理是基于接口的,沒有實作接口該類無法使用JDK代理,CGLIB登場。

「2、CGLIB動态代理」

CGLib采用非常底層的位元組碼技術,可以為一個類建立子類,并在子類中采用方法去技術攔截所有的父類方法的調用,并順勢織入橫切邏輯。

目标對象:

//目标對象RealSubject,cglib不需要定義目标類的統一接口
public class RealSubject {

    public void request() {
        System.out.println("real subject execute request");
    }

    public void hello() {
        System.out.println("hello");
    }
}

      

代理對象:實作MethodInterceptor,重寫intercept方法。

//代理對象
public class DemoMethodInterceptor implements MethodInterceptor{
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("before in cglib");
        Object result = null;
        try{
            result = proxy.invokeSuper(obj, args);
        }catch (Exception e){
            System.out.println("get ex:"+e.getMessage());
            throw e;
        }finally {
            System.out.println("after in cglib");
        }
        return result;
    }
}

      

用戶端調用:

//用戶端
public class Client {
    public static void main(String[] args){
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(RealSubject.class);
        enhancer.setCallback(new DemoMethodInterceptor());
        // 此刻,realSubject不是單純的目标類,而是增強過的目标類  
        RealSubject realSubject = (RealSubject) enhancer.create();
        realSubject.hello();
        realSubject.request()
    }
}

      

也可以通過匿名的方式:

 public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(SampleClass.class);
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
                System.out.println("before method run...");
                Object result = proxy.invokeSuper(obj, args);
                System.out.println("after method run...");
                return result;
            }
        });
        SampleClass sample = (SampleClass) enhancer.create();
        sample.test();
    }