代理模式(三) ---- 实现自定义代理逻辑的动态代理

上一篇我们实现了对所选择的接口的所有方法实现代理,但是代理逻辑却已经被写死了,自然不是很合适,这一篇我们便来解决这个问题,实现自定义代理逻辑的动态代理,实现之后我们的动态代理就已经很接近 JDK 自带的代理了。


  1. 要实现自定义代理逻辑,相信大家首先能想到的应该是定义一个接口 InvocationHandler ,我们所自定义的代理逻辑应该是实现了这个接口的逻辑类。
    InvocationHandler 代码:
    package dynamicProxy;
    
    import java.lang.reflect.Method;
    
    public interface InvocationHandler {
        void invoke(Object o ,Method method) ;
    }
    
  2. 定义一种代理逻辑,把目标对象跟方法传进来。这里还是采用之前的代理逻辑即时间代理。
    TimeHandler 代码:
    package dynamicProxy;
    
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    
    public class TimeHandler implements InvocationHandler {
        public TimeHandler(Object target) {
            this.target = target;
        }
    
        private Object target ;
    
        @Override
        public void invoke(Object o, Method method) {
    
            long startTime = System.currentTimeMillis() ;
    
            try {
                method.invoke(target, new Object[]{}) ;
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
    
            long endTime = System.currentTimeMillis() ;
            System.out.println("time : " + (endTime - startTime)) ;
    
        }
    }
    
    
    这里的 target 为被代理对象,method.invoke(target, new Object[]{}) ; 中的 Object 数组是执行该方法的参数,这里为了简便操作先定义一个空数组。
  3. 接下来我们理所应当想到的就是修改生成代理类的工具,把代理逻辑也传进去。这里需要修改的地方有几处:
    • 修改生成代理类的类,添加 newInstance 方法的参数 InvocationHandler:
    public static Object newInstance(Class inface,InvocationHandler h)
    
    • 修改代理类的生成代码 str,构造方法不再是所给定的代理类,而是 InvocationHandler
    "public class TankTimeProxy implements "+ inface.getName() + "{\n" +
    "    public TankTimeProxy(InvocationHandler h) {\n" +
    "        this.handler = h;\n" +
    "    }\n" +
    "\n" +
    "    private InvocationHandler handler ;\n" +
    "\n" +
    
    • 方法字符串中,原本是执行方法的 invoke 方法(md.invoke(this, new Object[]{}) ;),现在改为执行 handler 的 invoke 方法
    "           handler.invoke(this, md) ; \n" +
    
    • 修改构造器参数:
    Constructor constructor = c.getConstructor(InvocationHandler.class) ;
    Object m = constructor.newInstance(handler);
    

实际上到了这里,我们要实现的动态代理就已经完成了,可以完成对任意接口的所有方法实现我们自定义的代理逻辑。

这里再给出 Client 代码 :

 Tank tank = new Tank() ;
Moveable tankTimeProxy = (Moveable)Proxy.newInstance(Moveable.class, new TimeHandler(tank)) ;
tankTimeProxy.move();

代码结果:

结果

最后有几点需要说明:

  1. 为什么代理类的构造方法要改为 InvocationHandler?
    我们已经知道,要实现代理肯定要有被代理对象,而我们在定义代理逻辑类的时候,已经把被代理对象作为构造方法的参数传进来了,所以代理类的构造方法定义为 InvocationHandler ,既包括了代理逻辑,也包括了被代理对象。
  2. 代理字符串中的 handler.invoke(this, md) 其中的 this 怎么理解?
    this 关键字相信大家都有所了解,就是当前对象。那么在代理类字符串中,this 自然代表了代理对象。而我们代理逻辑中,这个对象暂时没有用到,但不代表以后我们不会用到。在 JDK 中,这个代理类的名字叫 $Proxy1 有兴趣的可以去看看。

你可能感兴趣的:(代理模式(三) ---- 实现自定义代理逻辑的动态代理)