代理模式 Java 實現

介紹

  • 什麼是代理模式
  • 靜態代理
  • JDK 自帶的動態代理
  • CGLIB 動態代理

代理模式

意圖:爲其他對象提供一種代理,以控制對這個對象的訪問。

例子:買火車票不一定要在火車站,去網上各個代理商那裏也可以

代碼思路:實體類 A 實現了接口 IA,而實體類 A 很複雜,那麼使用實體類 B 去實現接口 IA,通過實體類 B 調用實體類 A 去滿足功能。

注意事項:

1、和適配器模式的區別:適配器模式主要改變所考慮對象的接口,而代理模式不能改變所代理類的接口。

2、和裝飾器模式的區別:裝飾器模式爲了增強功能,而代理模式是爲了加以控制。

在這裏插入圖片描述

靜態代理

靜態代理其實就是在程序運行之前,提前寫好被代理方法的代理類,編譯後運行。

下面代理模式實現需要寫很多代碼,不建議使用。

// 接口 A
public interface IServiceA {
    void sayHello(String str);
}

// ServiceAImpl 實現接口 A
public class ServiceAImpl implements IServiceA {

    @Override
    public void sayHello(String str) {
        System.out.println(str);
    }
}

// 代理對象 ServiceProxyAImpl 也實現接口 A,並且構造函數必須傳入接口 A 的實例
public class ServiceProxyAImpl implements IServiceA {
    private IServiceA iServiceA;
    public ServiceProxyAImpl(IServiceA iServiceA) {
        this.iServiceA = iServiceA;
    }
    @Override
    public void sayHello(String str) {
        iServiceA.sayHello(str+ " is proxy ");
    }
}

// 進行測試
public class Main {
    public static void main(String[] args) {
        IServiceA realA = new ServiceAImpl();
        // 傳入真實對象
        IServiceA proxyA = new ServiceProxyAImpl(realA);
        realA.sayHello("realA");
        // 代理對象的調用
        proxyA.sayHello("proxyA");
    }
}

程序輸出 :
ServiceAImpl sayrealA
ServiceAImpl sayproxyA is proxy 

動態代理

通過反射機制,在運行時生成代理類

JDK 動態代理

被代理的類需要是接口實現,如果被代理的類沒有實現接口,就不能使用 JDK 動態代理

  1. java.lang.reflect.Proxy:生成動態代理類和對象;
  2. java.lang.reflect.InvocationHandler:通過 invoke 方法實現對真實角色的代理訪問。
// 接口 A
public interface IServiceA {
    void sayHello(String str);
}

// ServiceAImpl 實現接口 A
public class ServiceAImpl implements IServiceA {
    @Override
    public void sayHello(String str) {
        System.out.println(str);
    }
}

public class ProxyHandler implements InvocationHandler {
    private IServiceA serviceA;
    public ProxyHandler(IServiceA serviceA) {
        this.serviceA = serviceA;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        return method.invoke(serviceA, args);
    }
}

public class MainProxy {
    public static void main(String[] args) {
    	// 真實對象
        IServiceA serviceA = new ServiceAImpl();
        // 通過 Proxy 得到代理對象
        IServiceA proxyServiceA = (IServiceA) Proxy
            .newProxyInstance(serviceA.getClass().getClassLoader(), serviceA.getClass().getInterfaces(),
                new ProxyHandler(serviceA));
        serviceA.sayHello("serviceA");
        proxyServiceA.sayHello("proxy serviceA");
    }
}

程序輸出:
serviceA
proxy serviceA

CGLIB 動態代理

通過改變字節碼,對需要被代理的類,生成他的子類,子類去覆蓋被代理類的方法,其中 private、final 修飾的方法不會被重寫。

  1. 首先實現 MethodInterceptor,方法調用會被轉發到該類的 intercept() 方法。
  2. 通過 Enhancer 來指定要代理的目標對象、實際處理代理邏輯的對象,最終通過調用 create() 方法得到代理對象
// 接口 A
public interface IServiceA {
    void sayHello(String str);
}

// ServiceAImpl 實現接口 A
public class ServiceAImpl implements IServiceA {
    @Override
    public void sayHello(String str) {
        System.out.println(str);
    }
}

public class MyMethodInterceptor implements MethodInterceptor {

    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        return methodProxy.invokeSuper(o,objects);
    }
}

public class CGLIBMain {

    public static void main(String[] args) {
        IServiceA serviceA = new ServiceAImpl();
        serviceA.sayHello("serviceA");

        Enhancer enhancer = new Enhancer(); // 字節碼增強
        enhancer.setSuperclass(ServiceAImpl.class); // 設置父類
        enhancer.setCallback(new MyMethodInterceptor());  // 設置回調方法
        IServiceA proxyA = (IServiceA) enhancer.create();
        proxyA.sayHello("proxyA");
    }
}

程序輸出:
serviceA
proxyA

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章