程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> JAVA綜合教程 >> 深入淺出JDK動態代理(一),深入淺出jdk動態代理

深入淺出JDK動態代理(一),深入淺出jdk動態代理

編輯:JAVA綜合教程

深入淺出JDK動態代理(一),深入淺出jdk動態代理


1.何為代理
  代理,即代替主角完成一些額外的事情。例如,明星都有經紀人,明星參演電影之前,經紀人作為明星的代理人和出資方洽談片酬、排期等,而真正參與拍戲的還是明星本人,明星拍完戲後,由經紀人代理明星去清算片酬等。Java中的代理機制就是在目標方法執行前後執行一些額外的操作,如安全檢查、記錄日志等,Java中的代理分為靜態代理和動態代理。

2.靜態代理
  首先看一下靜態代理,直接上代碼,代碼模擬了登錄操作。

public interface LoginService {
    void login();
}
public class LoginServiceImpl implements LoginService {
    @Override
    public void login() {
        System.out.println("login");
    }
}
public class LoginServiceProxy implements LoginService {
    private LoginService loginService;
    public LoginServiceProxy(LoginService loginService) {
        this.loginService = loginService;
    }
    @Override
    public void login() {
        beforeLogin();
        loginService.login();
        afterLogin();
    }
    private void beforeLogin() {
        System.out.println("before login");
    }
    private void afterLogin() {
        System.out.println("after login");
    }
}
public class Client {
    @Test
    public void test() {
        LoginService loginService = new LoginServiceImpl();
        LoginService loginServiceProxy = new LoginServiceProxy(loginService);
        loginServiceProxy.login();
    }
}

輸出結果如下:

before login
login
after login

上面代碼實現的靜態代理很容易理解,使用聚合方式,在登錄操作前後執行額外的操作。靜態代理方式可以看得到具體代理類的代碼,且代碼由程序員編寫,在編譯之後會生成相應的class文件。使用靜態代理方式的缺點,如果需要對LoginService接口中有N個方法都代理,則需要在代理類中創建N個代理方法,並且需要編寫重復的代理操作代碼。

3.概念解釋
  目標接口,即對目標操作的抽象,如LoginService。
  目標類,即目標接口的實現類,如LoginServiceImpl。
  目標對象,即目標類的實例。
  代理類,即目標類的代理,如LoginServiceProxy。
  代理對象,即代理類的實例。

4.動態代理
  動態代理,即在運行時根據目標接口動態生成的代理類。動態代理方式生成的代理類在編譯後不會生成實際的class文件,而是在運行時動態生成類字節碼,並加載到JVM中使用。下面使用JDK的動態代理機制模擬登錄操作,具體代碼如下。

public interface LoginService {
    void login();
}
public class LoginServiceImpl implements LoginService {
    @Override
    public void login() {
        System.out.println("login");
    }
}
public class ProxyInvocationHandler implements InvocationHandler {
    private LoginService loginService;
    public ProxyInvocationHandler(LoginService loginService) {
        this.loginService = loginService;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        beforeLogin();
        Object invokeResult = method.invoke(loginService, args);
        afterLogin();
        return invokeResult;
    }
    private void beforeLogin() {
        System.out.println("before login");
    }
    private void afterLogin() {
        System.out.println("after login");
    }
}
public class Client {
    @Test
    public void test() {
        LoginService loginService = new LoginServiceImpl();
        ProxyInvocationHandler proxyInvocationHandler = new ProxyInvocationHandler(loginService);
        LoginService loginServiceProxy = (LoginService) Proxy.newProxyInstance(loginService.getClass().getClassLoader(), loginService.getClass().getInterfaces(), proxyInvocationHandler);
        loginServiceProxy.login();
        createProxyClassFile();
    }
    public static void createProxyClassFile() {
        String name = "LoginServiceProxy";
        byte[] data = ProxyGenerator.generateProxyClass(name, new Class[]{LoginService.class});
        try {
            FileOutputStream out = new FileOutputStream("/Users/" + name + ".class");
            out.write(data);
            out.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

  輸出結果如下。

before login
login
after login

  JDK動態代理方式實現代理的步驟如下:
  1.編寫目標接口;
  2.編寫目標類實現目標接口,實現目標方法的具體邏輯;
  3.編寫一個代理處理器類實現InvocationHandler接口,重寫invoke方法,用於指定運行時將生成的代理類需要完成的具體操作,包括beforeLogin和afterLogin。代理對象調用任何代理方法時都會調用這個invoke方法;
  4.創建代理對象,使用代理對象調用代理方法。

  上面的步驟中主要涉及以下兩個類:java.lang.reflect.InvocationHandler和java.lang.reflect.Proxy。InvocationHandler是一個接口,代理類的調用處理器,每個代理對象都具有一個關聯的調用處理器,用於指定動態生成的代理類需要完成的具體操作。該接口中有一個invoke方法,代理對象調用任何目標接口的方法時都會調用這個invoke方法,在這個方法中進行目標類的目標方法的調用。Proxy提供靜態方法用於創建動態代理類和代理類實例,同時,使用它提供的方法創建的代理類都是它的子類。這個類中主要關注newProxyInstance方法,該方法用於創建代理類對象,方法聲明如下:

public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)

loader參數用於指示使用哪個類加載器加載這個代理類;interfaces表示代理類實現的接口列表;h表示使用哪個調用處理器。

  後續文章《深入淺出JDK動態代理(二)》會深入源碼分析JDK動態代理生成的代理類是什麼樣,為什麼調用代理類的任何方法時都一定會調用invoke方法,值得期待!

  

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved