JDK Proxy 小試

demo

在本 demo 中我們將創(chuàng)建一個(gè) door 接口,實(shí)現(xiàn)對(duì)接口中所有方法的代理。
door.java

public interface Door {

    void enter();

    void out();

}

HomeDoorImpl.java

public class HomeDoorImpl implements Door{

    @Override
    public void enter() {
        System.out.println("enter the home door");
    }

    @Override
    public void out() {
        System.out.println("out the home door");
    }
}

main and proxy

public class Proxy implements InvocationHandler {

    private Object target;

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("start proxy");
        Object res = method.invoke(target, args);
        System.out.println("end proxy");
        return res;
    }


    public static void main(String[] args) {
        Door door;
        Proxy proxy = new Proxy(door = new HomeDoorImpl());
        door = (Door) java.lang.reflect.Proxy.newProxyInstance(door.getClass().getClassLoader(),
                door.getClass().getInterfaces(), proxy);
        door.enter();
        door.out();
    }

}

運(yùn)行

輸出

start proxy
enter the home door
end proxy
start proxy
out the home door
end proxy

僅代理某個(gè)方法

可以看到我們將 Door 接口中的所有方法都代理,但在實(shí)際業(yè)務(wù)中可能僅代理某些方法。
接下來(lái)做一些小改動(dòng)來(lái)支持這個(gè)功能。我們決定通過(guò)在方法上添加注解決定是否真正代理(PS:本質(zhì)上實(shí)現(xiàn)類(lèi)的所有方法都會(huì)被代理)
NeedProxy.java

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface NeedProxy {}

HomeDoorImpl.javaenter 方法添加注解

public class HomeDoorImpl implements Door{

    @NeedProxy
    @Override
    public void enter() {
        System.out.println("enter the home door");
    }

    @Override
    public void out() {
        System.out.println("out the home door");
    }
}

Proxy.java 使用 Map 集合保存了需要代理的方法,在 invoke function 時(shí)判斷是否執(zhí)行代理邏輯。

public class Proxy implements InvocationHandler {

    private HashMap<String, Object> proxyMethods = new HashMap<>();
    private Object target;

    public Proxy(Object target) {
        this.target = target;
        Method[] methods = target.getClass().getMethods();
        for (Method method : methods) {
            Annotation[] annotations = method.getAnnotations();
            for (Annotation annotation : annotations) {
                if (annotation.annotationType().equals(NeedProxy.class)) {
                    proxyMethods.put(method.getName(), Void.TYPE);
                    break;
                }
            }
        }
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (proxyMethods.containsKey(method.getName())) {
            return invoke0(proxy, method, args);
        }
        return method.invoke(target, args);
    }

    private Object invoke0(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException {
        System.out.println("start proxy");
        Object res = method.invoke(target, args);
        System.out.println("end proxy");
        return res;
    }


    public static void main(String[] args) {
        Door door;
        Proxy proxy = new Proxy(door = new HomeDoorImpl());
        door = (Door) java.lang.reflect.Proxy.newProxyInstance(door.getClass().getClassLoader(),
                door.getClass().getInterfaces(), proxy);
        door.enter();
        door.out();
    }

}

輸出

------start proxy-----
enter the home door
------end proxy-------
out the home door

實(shí)現(xiàn)一個(gè) before-after

真實(shí)業(yè)務(wù)上不可能所有方法都是一樣的代理邏輯(PS:logger 是可以使用同一個(gè)邏輯的)如何模擬實(shí)現(xiàn)一個(gè)類(lèi)似真實(shí)業(yè)務(wù)的 before-after,我們決定在 NeedProxyProxy 上下工夫。
BeforeAfter.java 定義一個(gè) before-after 接口,這里可以根據(jù)不同的業(yè)務(wù)邏輯實(shí)現(xiàn),after 方法頁(yè)可以包裝以下 result 再吐出去。

public interface BeforeAfter{
    void before(Object[] args);
    void after(Object result);
}

NeedProxy.java

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface NeedProxy {
    Class<? extends BeforeAfter> beforeAfter();
}

HomeDoorImpl.java

public class HomeDoorImpl implements Door{

    @NeedProxy(beforeAfter = HomeDoorEnterBeforeAfter.class)
    @Override
    public void enter() {
        System.out.println("enter the home door");
    }

    @Override
    public void out() {
        System.out.println("out the home door");
    }
}

HomeDoorEnterBeforeAfter.java

public class HomeDoorEnterBeforeAfter implements BeforeAfter {

    @Override
    public void before(Object[] args) {
        System.out.println("打開(kāi)室外燈");
    }

    @Override
    public void after(Object result) {
        System.out.println("關(guān)閉室外燈");
    }
}

Door.java 目前代理邏輯中還沒(méi)有一個(gè) HomeDoorEnterBeforeAfter 實(shí)例,可能 BeforeAfter 構(gòu)造方法需要依賴。下方采用反射構(gòu)造實(shí)例。

public class Proxy implements InvocationHandler {

    private HashMap<String, BeforeAfter> proxyMethods = new HashMap<>();
    private Object target;

    public Proxy(Object target) throws IllegalAccessException, InstantiationException {
        this.target = target;
        Method[] methods = target.getClass().getMethods();
        for (Method method : methods) {
            Annotation[] annotations = method.getAnnotations();
            for (Annotation annotation : annotations) {
                if (annotation.annotationType().equals(NeedProxy.class)) {
                    NeedProxy needProxy = (NeedProxy) annotation;
                    proxyMethods.put(method.getName(), needProxy.beforeAfter().newInstance());
                    break;
                }
            }
        }
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        BeforeAfter beforeAfter;
        if ((beforeAfter = proxyMethods.get(method.getName())) != null) {
            return invoke0(beforeAfter, proxy, method, args);
        }
        return method.invoke(target, args);
    }

    private Object invoke0(BeforeAfter beforeAfter, Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException {
        beforeAfter.before(args);
        Object res = method.invoke(target, args);
        beforeAfter.after(res);
        return res;
    }


    public static void main(String[] args) throws InstantiationException, IllegalAccessException {
        Door door;
        Proxy proxy = new Proxy(door = new HomeDoorImpl());
        door = (Door) java.lang.reflect.Proxy.newProxyInstance(door.getClass().getClassLoader(),
                door.getClass().getInterfaces(), proxy);
        door.enter();
        door.out();
    }

}

輸出

打開(kāi)室外燈
enter the home door
關(guān)閉室外燈
out the home door

小結(jié)

可以看到 JDK 的動(dòng)態(tài)代理實(shí)現(xiàn)代理顆粒度較為粗糙,并且需要入侵所有方法,性能上可能有所下降。讀者如果對(duì) Proxy 有興趣的話,可以進(jìn)一步了解 cglib 是如何實(shí)現(xiàn)代理的。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容