代理模式之靜態代理,JDK動態代理和cglib動態代理

代理模式,顧名思義,就是通過代理去完成某些功能。比如,你需要購買火車票,不想跑那麼遠到火車站售票窗口買,可以去附近的火車票代售點買,或者到攜程等第三方網站買。這個時候,我們就把火車站叫做目標對象或者委託對象,也可以叫被代理對象,而火車票代售點和攜程就叫做代理對象。

一、靜態代理

靜態代理是最簡單的代理模式。需要定義一個接口,然後委託類和代理類分別實現這個接口

//待實現的接口
public interface UserManager {
    public void getName(String name);
    public void getId(int id);
}
//委託類
public class UserManagerImpl implements UserManager {

    @Override
    public void getName(String name) {
        System.out.println("UserManagerImpl.getName:" + name);
    }

    @Override
    public void getId(int id) {
        System.out.println("UserManagerImpl.getId:" + id);
    }
}
//代理類
public class UserManagerProxy implements UserManager {

    UserManager userManager;

    public UserManagerProxy(UserManager userManager) {
        this.userManager = userManager;
    }

    @Override
    public void getName(String name) {
        System.out.println("before getName");
        userManager.getName(name);
        System.out.println("after getName");
    }

    @Override
    public void getId(int id) {
        userManager.getId(id);
    }

    public static void main(String[] args) {
        UserManagerProxy proxy = new UserManagerProxy(new UserManagerImpl());
        proxy.getName("zhangsan");
    }
}
//before getName
//UserManagerImpl.getName:zhangsan
//after getName

可以看到,在編譯成class之前,就已經確定了委託類UserManagerImpl和代理類UserManagerProxy。因此,才叫靜態代理。這樣雖然定義比較方便,實現也簡單,但是有一個弊端。當接口再新加一個方法時,委託類和代理類都需要同步地去實現方法,因此維護起來比較麻煩。而動態代理解決了這個問題。

二、JDK動態代理

動態代理分爲JDK動態代理和cglib動態代理。動態是指,代理類是通過反射等機制動態生成的,委託類和代理類的關係在運行時才確定。他們的主要區別就是,JDK動態代理需要實現接口,而cglib是通過繼承來實現的,不需要定義接口。

JDK動態代理,需要定義一個類去實現InvocationHandler接口

public class LogHandler implements InvocationHandler {

    private Object targetObj;

    public Object newProxyObject(Object targetObj){
        this.targetObj = targetObj;
        return Proxy.newProxyInstance(
                targetObj.getClass().getClassLoader(), //獲取委託類的類加載器
                targetObj.getClass().getInterfaces(), //獲取委託類實現的所有接口
                this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object ret;

        try {
            System.out.println("before method");
            ret = method.invoke(targetObj, args);
            System.out.println("after method");
        } catch (IllegalAccessException e) {
            e.printStackTrace();
            System.out.println("error");
            throw e;
        }

        return ret;
    }
}

public class TestProxy {
    public static void main(String[] args) {
        LogHandler logHandler = new LogHandler();
        UserManager o = (UserManager)logHandler.newProxyObject(new UserManagerImpl());
        o.getName("ls");
        o.getId(2);
    }
}
/**
運行結果如下:
before method
UserManagerImpl.getName:ls
after method
before method
UserManagerImpl.getId:2
after method
*/

JDK動態代理其實是在運行時動態生成了一個代理類去實現接口,只是隱藏了這個過程,我們不知道而已。

class $JDKProxy implements UserManager{}

需要注意的是,實現JDK動態代理的一個前提就是,需要定義一個接口,然後委託類去實現這個接口。那如果我不想定義接口,只定義一個委託類能不能實現呢?這就需要用到cglib代理了。(因爲cglib是通過繼承方式)

三、cglib動態代理

需要定義一個類實現MethodInterceptor接口(注意,這個類可不是代理類,也不是委託類哦)。

//委託類,不需要實現接口
public class CgTarget {
    public void getContent(){
        System.out.println("cglib被代理類getContent方法");
    }
}

public class CglibProxy implements MethodInterceptor {

    private Object target;

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("cglib開始");
        Object invoke = method.invoke(target, objects);
        System.out.println("cglib結束");
        return invoke;
    }
    //代理方法
    public Object getCglibProxy(Object target){
        this.target = target;
        Enhancer enhancer = new Enhancer();
        //設置增強類的父類,即被代理類
        enhancer.setSuperclass(target.getClass());
        //設置回調函數
        enhancer.setCallback(this);
        //返回創建的代理類
        return enhancer.create();
    }
}

public class TestCglib {
    public static void main(String[] args) {
        CglibProxy cglibProxy = new CglibProxy();
        CgTarget o = (CgTarget)cglibProxy.getCglibProxy(new CgTarget());
        o.getContent();
    }
}
/**
打印結果如下:
cglib開始
cglib被代理類getContent方法
cglib結束
*/

可以看到,cglib動態代理是通過Enhancer類的create方法創建了代理類。其實,其內部是通過繼承委託類來動態生成代理類的。它隱藏了以下過程

class $cglibProxy extends CgTarget{}

因此,委託類不能定義成final類型的,因爲final修飾的類是不能被繼承的。

瞭解spring AOP的同學應該知道,AOP是面向切面編程,在管理事物的時候會用到。其實,AOP就是通過動態代理來實現的,具體是用的JDK動態代理還是cglib動態代理,感興趣的小夥伴可以繼續深入研究哦。

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