帶你入門RPC之反向代理

黑白搬磚工發表於2020-04-07

1.前言

系統開發已經由單體服務轉向為一個一個的微小服務,微小服務的好處就是每個服務只需要關心自己內部的業務,當需要相關業務資料的時候,就會面臨服務呼叫的問題,服務呼叫其實也好解決,可以使用java自帶的HttpURLConnection進行遠端服務的呼叫,也可以使用HttpClient或者是OkHttp這樣的第三方客戶端進行遠端服務呼叫,同樣也可以使用高效能遠端呼叫框架Dubbo。

目前比較流行的微服務技術棧,可以使用基於Netty實現的Dubbo,或者使用基於Http實現的SpringCloud,不管哪種技術,為了實現遠端過程呼叫的便利性,使開發者只需要關注業務本身,而不需要關注呼叫細節,都採取了反向代理技術。反向代理技術在實現服務間透明呼叫起到了非常重要作用,既然重要,那麼就有必要和大家一起來溫習一下反向代理技術。

2.反向代理

2.1 代理的作用

代理的作用就是增強目標方法的能力,比如最常見的事務、Aop都是基於代理來實現的。當需要增強目標方法的能力,並且這些能力都是相同的,那麼就可以採取代理的方式進行實現。

2.2 代理的分類

  • 靜態代理:靜態代理需要為每個被代理的類建立一個代理類,當被代理的類過多的時候,就會導致代理類的增多,不便於維護。
  • 動態代理:動態代理不需要為每個被代理的類建立一個代理類,只需要一個全域性的代理類,在需要的時候動態生成,便於維護。

2.3 靜態代理

如圖可以看到被代理類實現的介面和被代理類其實就是我們開發的業務介面和業務介面實現類,在需要代理的情況下,代理類也要實現被代理類實現的介面,接下來我們來看一下程式碼的實現。

2.3.1 被代理類實現的介面

public interface HelloService {
    void hello();
}
複製程式碼

2.3.2 被代理類

public class HelloServiceImpl implements HelloService {
    public void hello() {
        System.out.println("你吃了嘛?");
    }
}
複製程式碼

2.3.3 代理類

public class HelloServiceStaticProxy implements HelloService {

    private HelloService helloService;

    public HelloServiceStaticProxy(HelloService helloService) {
        this.helloService = helloService;
    }

    public void hello() {
        System.out.println("你好,我是小王!");
        this.helloService.hello();
        System.out.println("好的,下次家裡聊!");
    }
}
複製程式碼

2.3.4 測試

@Test
public void staticProxy() {
    HelloServiceStaticProxy helloServiceStaticProxy = new HelloServiceStaticProxy(new HelloServiceImpl());
    helloServiceStaticProxy.hello();
}
複製程式碼
你好,我是小王!
你吃了嘛?
好的,下次家裡聊!
複製程式碼

2.3.5 小結

靜態代理要求代理類和被代理類實現同一個介面,代理物件需要持有被代理的目標物件,在代理物件實現介面方法前後新增增強邏輯並呼叫目標物件方法。

2.4 動態代理

2.4.1 動態代理實現技術

  • 基於jdk實現
  • 基於Cglib實現

2.4.2 基於jdk實現

2.4.2.1 建立代理類,實現InvocationHandler介面
public class JdkDynamicProxy implements InvocationHandler {

    private Object target;

    public JdkDynamicProxy(Object target) {
        this.target = target;
    }

    public Object getTarget() {
        return target;
    }

    public void setTarget(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("你好,我是小王!");
        Object result = method.invoke(target, args);
        System.out.println("好的,下次家裡聊!");
        return result;
    }
}
複製程式碼
2.4.2.2 建立代理工廠類
public class JdkDynamicProxyFactory {

    private JdkDynamicProxy jdkDynamicProxy;

    public JdkDynamicProxyFactory(JdkDynamicProxy helloServiceJdkDynamicProxy) {
        this.jdkDynamicProxy = helloServiceJdkDynamicProxy;
    }

    public Object getProxy() {
        Object target = jdkDynamicProxy.getTarget();
        return Proxy.newProxyInstance(jdkDynamicProxy.getClass().getClassLoader(), target.getClass().getInterfaces(), jdkDynamicProxy);
    }
}
複製程式碼
2.4.2.3 測試
@Test
public void test() {
    JdkDynamicProxy jdkDynamicProxy = new JdkDynamicProxy(new HelloServiceImpl());
    JdkDynamicProxyFactory proxyFactory = new JdkDynamicProxyFactory(jdkDynamicProxy);
    HelloService proxy = (HelloService) proxyFactory.getProxy();
    proxy.hello();
}
複製程式碼
你好,我是小王!
你吃了嘛?
好的,下次家裡聊!
複製程式碼

2.4.3 基於cglib實現

2.4.3.1 建立代理類,實現MethodInterceptor介面
public class CglibDynamicProxy implements MethodInterceptor {

    private Object target;

    public CglibDynamicProxy(Object target) {
        this.target = target;
    }

    public Object getTarget() {
        return target;
    }

    public void setTarget(Object target) {
        this.target = target;
    }

    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("你好,我是小王!");
        Object result = method.invoke(target, args);
        System.out.println("好的,下次家裡聊!");
        return result;
    }
}
複製程式碼
2.4.3.2 建立代理工廠類
public class CglibDynamicProxyFactory {

    private CglibDynamicProxy cglibDynamicProxy;

    public CglibDynamicProxyFactory(CglibDynamicProxy cglibDynamicProxy) {
        this.cglibDynamicProxy = cglibDynamicProxy;
    }

    public Object getProxy() {
        Enhancer enhancer = new Enhancer();
        enhancer.setInterfaces(cglibDynamicProxy.getTarget().getClass().getInterfaces());
        enhancer.setCallback(cglibDynamicProxy);
        return enhancer.create();
    }
}
複製程式碼
2.4.3.3 測試
@Test
public void test() {
    CglibDynamicProxy cglibDynamicProxy = new CglibDynamicProxy(new HelloServiceImpl());
    CglibDynamicProxyFactory proxyFactory = new CglibDynamicProxyFactory(cglibDynamicProxy);
    HelloService proxy = (HelloService) proxyFactory.getProxy();
    proxy.hello();
}
複製程式碼
你好,我是小王!
你吃了嘛?
好的,下次家裡聊!
複製程式碼

2.4.4 jdk動態代理與cglib動態代理的區別

  • jdk動態代理不需要引入第三方包,需要實現InvocationHandler介面,要求被代理物件必須實現介面
  • cglib動態代理需要引入第三方包,需要實現MethodInterceptor介面,被代理物件可以實現介面,也可以不實現介面

3. 總結

今天和大家一起溫習了一下代理的相關技術,代理分為:靜態代理和動態代理,動態代理的實現技術有:基於jdk來實現和基於cglib來實現,cglib可以針對沒有實現介面的目標物件進行代理。

代理技術是後續篇章的講解的基石,只有掌握代理技術,才能更好的去學習、實現遠端過程呼叫,希望你能牢牢掌握該門技術,可以讓自己在後續的篇章中無任何絆腳石。

相關文章