代理模式01-初體驗

定義

Proxy Pattern 是一種結構模式,指爲其餘對象提供一種代理以控制對這個對象的訪問。java

類圖

image

代理模式通常包含三種角色: node

Subject:
定義的RealSubject和Proxy的共有方法,這個類能夠是接口,也能夠是抽象類。這樣全部使用RealSubject的地方均可以用Proxy代替。框架

RealSubject:
被代理類,此類定義了真實的Subject實現。ide

Proxy:
代理類通常要持有一個被代理的對象的引用,並可能負責建立和刪除被代理對象。性能

靜態代理和動態代理

代理模式的實現大致上能夠分爲兩種,一種是靜態代理,一種是動態代理。兩種代理從JVM加載類的角度來說,本質上都是同樣的。this

靜態代理

上面的類圖就是傳統的靜態代理,代碼不貼了。(然而相關代碼接下來的demo裏有用到。)spa

優勢:.net

  • 實現簡單。

缺點:代理

  • 靜態代理只能經過手動完成被代理對象的注入。
  • 若是被代理類增長了新的方法,代理類須要同步增長,違背了開閉原則。
  • 一個代理類只能處理一個被代理類。

動態代理

JAVA的SDK中已經有了實現方法,Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h);
使用JDK動態代理須要注意,目標類必須實現的某個接口,若是某個類沒有實現接口則不能生成代理對象。 code

優勢:

  • 動態代理擴展性好。

    • 動態代理採用在運行時動態生成字節碼的方式,取消了被代理類的擴展限制,遵循開閉原則。
  • 被代理對象自動注入。

缺點:

  • 執行效率相對要低,每次都要反射動態調用控。
代碼示例
public static class SubjectInvocationHandler implements InvocationHandler {

    private Object object;

    public SubjectInvocationHandler(Object object) {
        this.object = object;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before 動態代理...");
        System.out.println(proxy.getClass().getName());
        System.out.println(this.object.getClass().getName());
        return method.invoke(object, args);
    }
}

public static void main(String[] args) {
    Subject o = (Subject) Proxy.newProxyInstance(Main.class.getClassLoader(), new Class[]{Subject.class}, new SubjectInvocationHandler(new RealSubject()));
    o.request();
}

Cglib動態代理

添加Maven依賴
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib-nodep</artifactId>
    <version>2.2</version>
</dependency>
代碼示例
public static void main(String[] args) {
    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(RealSubject.class);
    enhancer.setCallback((MethodInterceptor) (obj, method, args1, proxy) -> {
        System.out.println("before method run...");
        Object result = proxy.invokeSuper(obj, args1);
        System.out.println("after method run...");
        return result;
    });
    RealSubject subject = (RealSubject) enhancer.create();
    subject.request();
}

Cglib原理是針對目標類生成一個子類,覆蓋其中的全部方法,因此目標類和方法不能聲明爲final類型。
Cglib採用ASM框架寫Class字節碼,實現比JDK複雜,因此生產代理類比JDK動態代理效率低。
JDK是採用反射機制調用的,而CGLib經過FastClass機制直接調用方法,因此只需效率更高。

FastClass

FastClass不使用反射類(Constructor或Method)來調用委託類方法,而是動態生成一個新的類(繼承FastClass),向類中寫入委託類實例直接調用方法的語句,用模板方式解決Java語法不支持問題,同時改善Java反射性能。

動態類爲委託類方法調用語句創建索引,使用者根據方法簽名(方法名+參數類型)獲得索引值,再經過索引值進入相應的方法調用語句,獲得調用結果。

Spring中的代理選擇原則

  • 當Bean有實現接口時,Spring就會調用JDK動態代理。
  • 當Bean沒有實現接口時,Spring會選擇CGLib代理。
  • Spring能夠經過配置強制使用CGLib代理。

補充閱讀

警戒動態代理致使的Metaspace內存泄漏問題

https://blog.csdn.net/xyghehe...

相關文章
相關標籤/搜索