美文网首页
JDK Proxy 小试

JDK Proxy 小试

作者: leeehao | 来源:发表于2020-08-16 02:53 被阅读0次

    demo

    在本 demo 中我们将创建一个 door 接口,实现对接口中所有方法的代理。
    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();
        }
    
    }
    

    运行

    输出

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

    仅代理某个方法

    可以看到我们将 Door 接口中的所有方法都代理,但在实际业务中可能仅代理某些方法。
    接下来做一些小改动来支持这个功能。我们决定通过在方法上添加注解决定是否真正代理(PS:本质上实现类的所有方法都会被代理)
    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 时判断是否执行代理逻辑。

    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
    

    实现一个 before-after

    真实业务上不可能所有方法都是一样的代理逻辑(PS:logger 是可以使用同一个逻辑的)如何模拟实现一个类似真实业务的 before-after,我们决定在 NeedProxyProxy 上下工夫。
    BeforeAfter.java 定义一个 before-after 接口,这里可以根据不同的业务逻辑实现,after 方法页可以包装以下 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("打开室外灯");
        }
    
        @Override
        public void after(Object result) {
            System.out.println("关闭室外灯");
        }
    }
    

    Door.java 目前代理逻辑中还没有一个 HomeDoorEnterBeforeAfter 实例,可能 BeforeAfter 构造方法需要依赖。下方采用反射构造实例。

    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();
        }
    
    }
    

    输出

    打开室外灯
    enter the home door
    关闭室外灯
    out the home door
    

    小结

    可以看到 JDK 的动态代理实现代理颗粒度较为粗糙,并且需要入侵所有方法,性能上可能有所下降。读者如果对 Proxy 有兴趣的话,可以进一步了解 cglib 是如何实现代理的。

    相关文章

      网友评论

          本文标题:JDK Proxy 小试

          本文链接:https://www.haomeiwen.com/subject/hpnzdktx.html