1. 程式人生 > >代理模式-靜態代理與動態代理

代理模式-靜態代理與動態代理

應用 代碼 creat HR print tps 結果 inter OS

簡介

  • 首先感謝沽泡學院 tom 老師
  • 代理模式是一種結構型模式
  • 代理模式就是代理對象幫被代理對象處理一些問題, 類似中介, 客戶只要結果, 中介怎麽搞是他的事兒, 他可能再處理過程中賺外快什麽的
  • 代理模式的應用: spring中的aop, 日常工作中記錄日誌, 統計時間,權限控制等
  • 這裏我們使用一個客戶端代理訪問google舉例, 具體細節可能不合適, 意會、意會、意會...

靜態代理

/**
 * 一個服務器接口, 服務器有很多功能, 可以用來路由, 建站等...
 */
public interface Server {
    boolean internetgoogle(Boolean ispass);
    boolean internetbaidu(Boolean ispass);
}
/**
 * @ClassName: HKProxyServer
 * @description [靜態代理類, 幫助被代理對象訪問url, 靜態代理類和被代理對象實現同一接口, 唯一的區別就是代理類持有被代理對象引用, ]
 *  每次修改接口, 代理類和被代理類都要實現相同的方法, 靜態代理, 要做的事情已經是確定的, 只是表面上換了個人去做
 * @create 2018-03-31 上午7:56
 **/
public class HKProxyServer implements Server{

    //傳入客戶端信息
    private Server client;

    public HKProxyServer(Server client) {
        this.client = client;
    }

    @Override
    public boolean internetgoogle(Boolean ispass) {
        System.out.println("香港代理: ");
        return client.internetgoogle(true);
    }

    @Override
    public boolean internetbaidu(Boolean ispass) {
        System.out.println("香港代理: ");
        return client.internetbaidu(true);
    }
}
/**
 * @ClassName: ChinaServer
 * @description [描述該類的功能]
 * @create 2018-03-31 上午7:45
 **/
public class ChinaClient implements Server {

    @Override
    public boolean internetgoogle(Boolean ispass) {
        if(ispass != null && ispass){
            System.out.println(" 請求地址: www.google.com 通過");
            return true ;
        }else{
            System.out.println(" 請求地址: www.google.com 拒絕");
            return false;
        }
    }

    @Override
    public boolean internetbaidu(Boolean ispass) {
        System.out.println(" 請求地址: www.baidu.com 通過");
        return true;
    }
}
public class AccessGoogle {

    @Test
    public void tryToAccessGoogle(){
        Server chinaServer = new ChinaClient();
        //訪問不了google
        Assert.assertFalse(chinaServer.internetgoogle(null));
        //可以訪問baidu
        Assert.assertTrue(chinaServer.internetbaidu(null));

        System.out.println("--------------------------------------------------通過代理訪問--------------------------------------------------");
        Server proxyServer = new HKProxyServer(chinaServer);
        //可以訪問google
        Assert.assertTrue(proxyServer.internetgoogle(null));
        //可以訪問baidu
        Assert.assertTrue(proxyServer.internetbaidu(null));

    }
}

jdk實現動態代理

/**
 * @ClassName: HKProxyServer
 * @description [使用jdk動態代理實現, 不用實現與被代理類相同的接口, 接口增加方法也與我無關, 動態調用被代理類方法,
 *                  在調用方法前我可以輸出日誌, aop, 但是調用的方法的對象是傳入的client對象]
 * @create 2018-03-31 上午7:56
 **/
public class HKProxyServer implements InvocationHandler {

    //傳入客戶端信息
    private Server client;

    public HKProxyServer(Server client) {
        this.client = client;
    }

    public Object getInstence(){
        Class<?> clazz = client.getClass();
        //用來生成一個新的對象(字節碼重組來實現)
        return Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);

        //如果直接返回傳入對象就不會調用下面的invoke方法
//        return client;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        args = new Object[]{true};//代理執行方法前可以搞事情
        System.out.println("mylog +++++++++ 動態代理調用方法 " + method.getName());
        return method.invoke(this.client, args);
    }
}
/**
 * @ClassName: ChinaServer
 * @description [描述該類的功能]
 * @create 2018-03-31 上午7:45
 **/
public class ChinaClient implements Server {

    @Override
    public boolean internetgoogle(Boolean ispass) {
        System.out.println(this);
        if(ispass){
            System.out.println("請求地址: www.google.com 通過");
            return true ;
        }else{
            System.out.println("請求地址: www.google.com 拒絕");
            return false;
        }
    }

    @Override
    public boolean internetbaidu(Boolean ispass) {
        System.out.println(this);
        System.out.println("請求地址: www.baidu.com 通過");
        return true;
    }
}
public class AccessGoogle {

    @Test
    public void tryToAccessGoogle(){
        //代理對象內部使用傳入de被代理對象調用方法(等價 new Object().methodxx())
        Server chinaClient = new ChinaClient();
        Server proxyServer = (Server)new HKProxyServer(chinaClient).getInstence();
        Assert.assertTrue(proxyServer.internetgoogle(false));
        Assert.assertTrue(proxyServer.internetbaidu(true));

        Assert.assertFalse(chinaClient.equals(proxyServer));
    }
}

cglib實現動態代理

/**
 * @ClassName: HKProxyServer
 * @description [使用cglib實現動態代理, 客戶端都不用創建對象, 對象由框架創建載入, 體現在客戶端像是用接口調用方法]
 * @create 2018-03-31 上午7:56
 **/
public class HKProxyServer implements MethodInterceptor {

    public Object getInstence(Class<?> clazz){

        Enhancer enhancer = new Enhancer();
        //要把哪個設置為即將生成的新類父類
        enhancer.setSuperclass(clazz);

        enhancer.setCallback(this);

        return  enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("mylog +++++++++++++++ cglib代理實現: 調用方法" + method.getName());
        return methodProxy.invokeSuper(o,objects);
    }
}
public class ChinaClient implements Server {

    @Override
    public boolean internetgoogle(Boolean ispass) {
        System.out.println(this);
        if(ispass){
            System.out.println("請求地址: www.google.com 通過");
            return true ;
        }else{
            System.out.println("請求地址: www.google.com 拒絕");
            return false;
        }
    }

    @Override
    public boolean internetbaidu(Boolean ispass) {
        System.out.println(this);
        System.out.println("請求地址: www.baidu.com 通過");
        return true;
    }
}
public class AccessGoogle {

    @Test
    public void tryToAccessGoogle(){
        //不需要再new對象,傳入chinaclient.class即可, 然後使用接口調用方法, 內部創建了個新的子類對象來調用指定方法
        Server proxyServer = (Server)new HKProxyServer().getInstence(ChinaClient.class);
        Assert.assertTrue(proxyServer.internetgoogle(true));
        Assert.assertTrue(proxyServer.internetbaidu(true));
    }
}

三種代理方式的分析

  • 靜態代理: 要做的事情已經是確定的, 只是表面上換了個人去做,代理對象和被代理對象實現同樣的接口,實際通過傳入對象調用
  • jdk動態代理: 與靜態代理效果相同, 但是java通過反射實現代理類不用實現與被代理對象同樣的接口, 也可以根據傳入方法調用被代理對象的方法, 傳入對象調用
  • cglib動態代理: 只需要傳入被代理對象的類名, 內部會實現創建對象到調用的過程,客戶端不需要創建具體的對象, 接口調用方法

代碼路徑

https://github.com/offline7LY/designpattern

代理模式-靜態代理與動態代理