美文网首页
jdk动态代理

jdk动态代理

作者: 尉昌达 | 来源:发表于2020-06-11 23:43 被阅读0次
/**
 * 如果想让LogProxy可以重用,不仅可以代理Tank,还可以代理任何其他可以代理的类型 Object
 * (毕竟日志记录,时间计算是很多方法都需要的东西),这时该怎么做呢?
 * 分离代理行为与被代理对象
 * 使用jdk的动态代理
 *     通过反射观察生成的代理对象
 *     jdk反射生成代理必须面向接口,这是由Proxy的内部实现决定的
 */
public class Tank implements Movable {

    /**
     * 模拟坦克移动了一段儿时间
     */
    @Override
    public void move() {
        System.out.println("Tank moving claclacla...");
        try {
            Thread.sleep(new Random().nextInt(10000));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        Tank tank = new Tank();

        //reflection 通过二进制字节码分析类的属性和方法

        Movable m = (Movable)Proxy.newProxyInstance(Tank.class.getClassLoader(),
                new Class[]{Movable.class}, //tank.class.getInterfaces()  被代理类需要实现的接口
                new LogHander(tank)   //代理类处理逻辑
        );

        m.move();
    }
}

class LogHander implements InvocationHandler {

    Tank tank;

    public LogHander(Tank tank) {
        this.tank = tank;
    }
    //getClass.getMethods[]
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("method " + method.getName() + " start..");
        Object o = method.invoke(tank, args);
        System.out.println("method " + method.getName() + " end!");
        return o;
    }
}



interface Movable {
    void move();
}

生成代理类文件$Proxy0.class

public static void main(String[] args) {
        Tank tank = new Tank();
        //jdk8 加入该语句会在根目录相应文件夹生成class文件
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true"); 

        Movable m = (Movable)Proxy.newProxyInstance(Tank.class.getClassLoader(),
                new Class[]{Movable.class}, //tank.class.getInterfaces()
                new LogHander(tank)
        );
        m.move();

    }

用反编译工具打开class文件

package com.msb.test.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

final class $Proxy0 extends Proxy implements Movable {
  private static Method m1;
  
  private static Method m3;
  
  private static Method m2;
  
  private static Method m0;
  
  public $Proxy0(InvocationHandler paramInvocationHandler) {
    super(paramInvocationHandler);
  }
  
  public final boolean equals(Object paramObject) {
    try {
      return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
    } catch (Error|RuntimeException error) {
      throw null;
    } catch (Throwable throwable) {
      throw new UndeclaredThrowableException(throwable);
    } 
  }
  
  public final void move() {
    try {
      this.h.invoke(this, m3, null);
      return;
    } catch (Error|RuntimeException error) {
      throw null;
    } catch (Throwable throwable) {
      throw new UndeclaredThrowableException(throwable);
    } 
  }
  
  public final String toString() {
    try {
      return (String)this.h.invoke(this, m2, null);
    } catch (Error|RuntimeException error) {
      throw null;
    } catch (Throwable throwable) {
      throw new UndeclaredThrowableException(throwable);
    } 
  }
  
  public final int hashCode() {
    try {
      return ((Integer)this.h.invoke(this, m0, null)).intValue();
    } catch (Error|RuntimeException error) {
      throw null;
    } catch (Throwable throwable) {
      throw new UndeclaredThrowableException(throwable);
    } 
  }
  
  static {
    try {
      m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
      m3 = Class.forName("com.msb.test.proxy.Movable").getMethod("move", new Class[0]);
      m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
      m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
      return;
    } catch (NoSuchMethodException noSuchMethodException) {
      throw new NoSuchMethodError(noSuchMethodException.getMessage());
    } catch (ClassNotFoundException classNotFoundException) {
      throw new NoClassDefFoundError(classNotFoundException.getMessage());
    } 
  }
}

Proxy 类中一个构造方法

 protected Proxy(InvocationHandler h) {
        Objects.requireNonNull(h);
        this.h = h;
    }
//前面Tank类中调用这个方法 把new LogHander(tank) 赋值给 Proxy的InvocationHandler h了。
Movable m = (Movable)Proxy.newProxyInstance(Tank.class.getClassLoader(),
                new Class[]{Movable.class}, //tank.class.getInterfaces()
                new LogHander(tank)
        );
 m.move();

//m.move(); 那么就是$Proxy0类中的move方法
public final void move() {
    try {
    //这边就是调用了LogHander的invoke方法
      this.h.invoke(this, m3, null);
      return;
    } catch (Error|RuntimeException error) {
      throw null;
    } catch (Throwable throwable) {
      throw new UndeclaredThrowableException(throwable);
    } 
  }

//LogHander
@Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("method " + method.getName() + " start..");
        // 这里调用tank.move().
        Object o = method.invoke(tank, args);
        System.out.println("method " + method.getName() + " end!");
        return o;
    }

//上面
m3 = Class.forName("com.msb.test.proxy.Movable").getMethod("move", new Class[0]);

最后输出

method move start..
Tank moving claclacla...
method move end!

流程

image.png

上面的asm框架可以直接修改字节码文件

相关文章

网友评论

      本文标题:jdk动态代理

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