您的位置:首页 >新闻资讯 > 正文

JAVA的JDK动态代理实现

来源:互联网 作者:admin 时间:2019-10-16 16:09:24

  JAVA代理模式的类型,有动态与静态之分,而其中动态代理分为两种:JDK动态代理和CGLIB动态代理。后一种代理方式我们在之前的文章进行过介绍了,下面是JDK动态代理的介绍。相关阅读:JAVA实现CGLIB动态代理的教程


JAVA的JDK动态代理实现


  JDK动态代理


  JDK动态代理需先声明一个代理类和目标类之间的中间类,此中间类需要实现jdk中的一个接口InvocationHandler。


  源码如下:


  package java.lang.reflect;

  public interface InvocationHandler {

  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;

  }


  产生代理对象的中间类


  import java.lang.reflect.InvocationHandler;

  import java.lang.reflect.Method;

  import java.lang.reflect.Proxy;

  public class JDKProxy implements InvocationHandler {

  private Object target; //持有目标对象的引用

  public JDKProxy(Object target){

  this.target = target;

  }

  //创建代理对象

  public Object createProxy(){

  //1.得到目标对象的classloader

  ClassLoader classLoader = target.getClass().getClassLoader();

  //2.得到目标对象的实现接口的class[]

  Class<?>[] interfaces = target.getClass().getInterfaces();

  //3.第三个参数需要一个实现InvocationHandler接口的对象

  //3-1.第一种写法,让当前类实现InvocationHandler,第三个参数写this

  return Proxy.newProxyInstance(classLoader, interfaces, this);

  //3-2.第二种写法,第三个参数用匿名内部类的形式,先注释掉

  /*return Proxy.newProxyInstance(classLoader, interfaces, new InvocationHandler() {

  @Override

  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

  long startTime = System.currentTimeMillis();

  System.out.println("开始记录时间");

  Object ret = method.invoke(target, args); //执行目标对象方法,此处写target如果写proxy会死循环直到内存溢出

  long endTime = System.currentTimeMillis();

  System.out.println("保存用户信息方法耗时" + (endTime-startTime) + "毫秒");

  return ret;

  }

  });*/

  }

  /*

  在代理实例上执行目标对象的方法

  参数1 就是代理对象,一般不使用

  参数2 它调用的方法的Method对象

  参数3 调用的方法的参数

  */

  @Override

  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

  long startTime = System.currentTimeMillis();

  System.out.println("开始记录时间");

  Object ret = method.invoke(target, args);//执行目标对象方法,此处写target如果写proxy会死循环直到内存溢出

  long endTime = System.currentTimeMillis();

  System.out.println("保存用户信息方法耗时" + (endTime-startTime) + "毫秒");

  return ret;

  }

  }


  客户端public class ProxyTest {

  @Test

  public void test1() {

  UserService userService = new UserServiceImpl(); //1.创建目标对象

  JDKProxy factory = new JDKProxy(userService); // 2.通过JKDProxy完成代理对象创建

  UserService userServiceProxy = (UserService)factory.createProxy();

  userServiceProxy.saveUser(new User("1","张三"));

  }

  }


       JDK动态代理中,需要关注的两点:


       1、Proxy.newProxyInstance(classLoader, interfaces, this); 底层是怎么创建的代理对象


       2、invoke方法是什么时候执行的,谁来调用的此方法<font color=red>解析1>></font>怎么产生代理对象:@CallerSensitive


  public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)

  throws IllegalArgumentException

  {

  if (h == null) {

  throw new NullPointerException();

  }

  final Class<?>[] intfs = interfaces.clone();

  final SecurityManager sm = System.getSecurityManager();

  if (sm != null) {

  checkProxyAccess(Reflection.getCallerClass(), loader, intfs);

  }

  /*

  * Look up or generate the designated proxy class.

  */

  Class<?> cl = getProxyClass0(loader, intfs);

  /*

  * Invoke its constructor with the designated invocation handler.

  */

  try {

  final Constructor<?> cons = cl.getConstructor(constructorParams);

  final InvocationHandler ih = h;

  if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) {

  // create proxy instance with doPrivilege as the proxy class may

  // implement non-public interfaces that requires a special permission

  return AccessController.doPrivileged(new PrivilegedAction<Object>() {

  public Object run() {

  return newInstance(cons, ih);

  }

  });

  } else {

  return newInstance(cons, ih);

  }

  } catch (NoSuchMethodException e) {

  throw new InternalError(e.toString());

  }

  }

  //继续看newInstance方法

  private static Object newInstance(Constructor<?> cons, InvocationHandler h) {

  try {

  return cons.newInstance(new Object[] {h} );

  } catch (IllegalAccessException | InstantiationException e) {

  throw new InternalError(e.toString());

  } catch (InvocationTargetException e) {

  Throwable t = e.getCause();

  if (t instanceof RuntimeException) {

  throw (RuntimeException) t;

  } else {

  throw new InternalError(t.toString());

  }

  }


  由此可以看出创建代理对象,是利用反射,先获取目标对象的构造器,然后在通过构造反射生成代理对象<font color=red>解析2>></font>invoke方法什么时候被调用:我们通过一个工具类把生成的代理对象的字节码输出到磁盘,然后通过反编译来查看代理对象有哪些内容工具类如下:public class ProxyGeneratorUtils {

  /**

  * 把代理类的字节码写到硬盘上

  * @param fileName 文件名

  * @param path 路径信息

  * @param clazz 目标类的接口数组

  */

  public static void writeProxyClassToHardDisk(String fileName, String path, Class<?>[] clazz) {

  byte[] classFile = ProxyGenerator.generateProxyClass(fileName, clazz);

  FileOutputStream out = null;

  try {

  out = new FileOutputStream(path);

  out.write(classFile);

  out.flush();

  } catch (Exception e) {

  e.printStackTrace();

  } finally {

  try {

  out.close();

  } catch (IOException e) {

  e.printStackTrace();

  }

  }

  }

  //主方法

  public static void main(String[] args) {

  ProxyGeneratorUtils.writeProxyClassToHardDisk("$JDKProxy1","F:/$JDKProxy1.class",UserServiceImpl.class.getInterfaces());

  }

  }运行main方法生成代理对象字节码文件,用JD.exe反编译打开如下public final class $JDKProxy1 extends Proxy implements UserService {

  private static Method m1;

  private static Method m3;

  private static Method m0;

  private static Method m2;

  public $JDKProxy1(InvocationHandler arg0) throws {

  super(arg0);

  }

  public final void saveUser(User arg0) throws {

  try {

  super.h.invoke(this, m3, new Object[]{arg0});

  } catch (RuntimeException | Error arg2) {

  throw arg2;

  } catch (Throwable arg3) {

  throw new UndeclaredThrowableException(arg3);

  }

  }

  ...


JAVA的JDK动态代理实现


  代理实现的部分到这里就告一段落了,希望这个系列的解说能够让大家对JAVA掌握更多。


相关文章内容简介