文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

在Java中实现JDK动态代理的原理是什么

2023-05-31 10:00

关注

在Java中实现JDK动态代理的原理是什么?相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。

一、什么是代理?

代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。

代理模式UML图:

在Java中实现JDK动态代理的原理是什么

简单结构示意图:

在Java中实现JDK动态代理的原理是什么

为了保持行为的一致性,代理类和委托类通常会实现相同的接口,所以在访问者看来两者没有丝毫的区别。通过代理类这中间一层,能有效控制对委托类对象的直接访问,也可以很好地隐藏和保护委托类对象,同时也为实施不同控制策略预留了空间,从而在设计上获得了更大的灵活性。Java 动态代理机制以巧妙的方式近乎完美地实践了代理模式的设计理念。

二、Java 动态代理类

Java动态代理类位于java.lang.reflect包下,一般主要涉及到以下两个类:

(1)Interface InvocationHandler:该接口中仅定义了一个方法

publicobject invoke(Object obj,Method method, Object[] args)

在实际使用时,第一个参数obj一般是指代理类,method是被代理的方法,如上例中的request(),args为该方法的参数数组。这个抽象方法在代理类中动态实现。

(2)Proxy:该类即为动态代理类,其中主要包含以下内容:

protected Proxy(InvocationHandler h) :构造函数,用于给内部的h赋值。

static Class getProxyClass (ClassLoaderloader, Class[] interfaces) :获得一个代理类,其中loader是类装载器,interfaces是真实类所拥有的全部接口的数组。

static Object newProxyInstance(ClassLoaderloader, Class[] interfaces, InvocationHandler h) :返回代理类的一个实例,返回后的代理类可以当作被代理类使用(可使用被代理类的在Subject接口中声明过的方法)

所谓DynamicProxy是这样一种class:它是在运行时生成的class,在生成它时你必须提供一组interface给它,然后该class就宣称它实现了这些 interface。你当然可以把该class的实例当作这些interface中的任何一个来用。当然,这个DynamicProxy其实就是一个Proxy,它不会替你作实质性的工作,在生成它的实例时你必须提供一个handler,由它接管实际的工作。

在使用动态代理类时,我们必须实现InvocationHandler接口

通过这种方式,被代理的对象(RealSubject)可以在运行时动态改变,需要控制的接口(Subject接口)可以在运行时改变,控制的方式(DynamicSubject类)也可以动态改变,从而实现了非常灵活的动态代理关系。

动态代理步骤:

       1.创建一个实现接口InvocationHandler的类,它必须实现invoke方法

       2.创建被代理的类以及接口

       3.通过Proxy的静态方法

           newProxyInstance(ClassLoaderloader, Class[] interfaces, InvocationHandler h)创建一个代理

       4.通过代理调用方法

三、JDK的动态代理怎么使用?

1、需要动态代理的接口:

package jiankunking;   public interface Subject {    public String SayHello(String name);     public String SayGoodBye(); }

2、需要代理的实际对象

package jiankunking;   public class RealSubject implements Subject {     public String SayHello(String name)  {  return "hello " + name;  }     public String SayGoodBye()  {  return " good bye ";  } }

3、调用处理器实现类(有木有感觉这里就是传说中的AOP啊)

package jiankunking;  import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method;    public class InvocationHandlerImpl implements InvocationHandler {     private Object subject;     public InvocationHandlerImpl(Object subject)  {  this.subject = subject;  }     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable  {  //在代理真实对象前我们可以添加一些自己的操作  System.out.println("在调用之前,我要干点啥呢?");   System.out.println("Method:" + method);   //当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用  Object returnValue = method.invoke(subject, args);   //在代理真实对象后我们也可以添加一些自己的操作  System.out.println("在调用之后,我要干点啥呢?");   return returnValue;  } }

4、测试

package jiankunking;  import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy;   public class DynamicProxyDemonstration {  public static void main(String[] args)  {  //代理的真实对象  Subject realSubject = new RealSubject();     InvocationHandler handler = new InvocationHandlerImpl(realSubject);   ClassLoader loader = realSubject.getClass().getClassLoader();  Class[] interfaces = realSubject.getClass().getInterfaces();    Subject subject = (Subject) Proxy.newProxyInstance(loader, interfaces, handler);   System.out.println("动态代理对象的类型:"+subject.getClass().getName());   String hello = subject.SayHello("jiankunking");  System.out.println(hello); // String goodbye = subject.SayGoodBye(); // System.out.println(goodbye);  }  }

5、输出结果如下:

演示demo下载地址:http://xiazai.jb51.net/201707/yuanma/DynamicProxyDemo(jb51.net).rar

四、动态代理怎么实现的?

从使用代码中可以看出,关键点在:

Subject subject = (Subject) Proxy.newProxyInstance(loader, interfaces, handler);

通过跟踪提示代码可以看出:当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用。

也就是说,当代码执行到:

subject.SayHello("jiankunking")这句话时,会自动调用InvocationHandlerImpl的invoke方法。这是为啥呢?

=======横线之间的是代码跟分析的过程,不想看的朋友可以直接看结论============

以下代码来自:JDK1.8.0_92

既然生成代理对象是用的Proxy类的静态方newProxyInstance,那么我们就去它的源码里看一下它到底都做了些什么?

 @CallerSensitive public static Object newProxyInstance(ClassLoader loader,    Class<?>[] interfaces,    InvocationHandler h)  throws IllegalArgumentException  {  //检查h 不为空,否则抛异常  Objects.requireNonNull(h);   final Class<?>[] intfs = interfaces.clone();  final SecurityManager sm = System.getSecurityManager();  if (sm != null) {  checkProxyAccess(Reflection.getCallerClass(), loader, intfs);  }     Class<?> cl = getProxyClass0(loader, intfs);     try {  if (sm != null) {  checkNewProxyPermission(Reflection.getCallerClass(), cl);  }  //获取代理对象的构造方法(也就是$Proxy0(InvocationHandler h))  final Constructor<?> cons = cl.getConstructor(constructorParams);  final InvocationHandler ih = h;  if (!Modifier.isPublic(cl.getModifiers())) {  AccessController.doPrivileged(new PrivilegedAction<Void>() {   public Void run() {   cons.setAccessible(true);   return null;   }  });  }  //生成代理类的实例并把InvocationHandlerImpl的实例传给它的构造方法  return cons.newInstance(new Object[]{h});  } catch (IllegalAccessException|InstantiationException e) {  throw new InternalError(e.toString(), e);  } catch (InvocationTargetException e) {  Throwable t = e.getCause();  if (t instanceof RuntimeException) {  throw (RuntimeException) t;  } else {  throw new InternalError(t.toString(), t);  }  } catch (NoSuchMethodException e) {  throw new InternalError(e.toString(), e);  }  }

我们再进去getProxyClass0方法看一下:

  private static Class<?> getProxyClass0(ClassLoader loader,    Class<?>... interfaces) {  if (interfaces.length > 65535) {  throw new IllegalArgumentException("interface limit exceeded");  }   // If the proxy class defined by the given loader implementing  // the given interfaces exists, this will simply return the cached copy;  // otherwise, it will create the proxy class via the ProxyClassFactory  return proxyClassCache.get(loader, interfaces);  }

 真相还是没有来到,继续,看一下proxyClassCache

  private static final WeakCache<ClassLoader, Class<?>[], Class<?>>  proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

奥,原来用了一下缓存啊

那么它对应的get方法啥样呢?

  public V get(K key, P parameter) {  Objects.requireNonNull(parameter);   expungeStaleEntries();   Object cacheKey = CacheKey.valueOf(key, refQueue);   // lazily install the 2nd level valuesMap for the particular cacheKey  ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);  if (valuesMap == null) {  //putIfAbsent这个方法在key不存在的时候加入一个值,如果key存在就不放入  ConcurrentMap<Object, Supplier<V>> oldValuesMap  = map.putIfAbsent(cacheKey,    valuesMap = new ConcurrentHashMap<>());  if (oldValuesMap != null) {  valuesMap = oldValuesMap;  }  }   // create subKey and retrieve the possible Supplier<V> stored by that  // subKey from valuesMap  Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));  Supplier<V> supplier = valuesMap.get(subKey);  Factory factory = null;   while (true) {  if (supplier != null) {  // supplier might be a Factory or a CacheValue<V> instance  V value = supplier.get();  if (value != null) {   return value;  }  }  // else no supplier in cache  // or a supplier that returned null (could be a cleared CacheValue  // or a Factory that wasn't successful in installing the CacheValue)   // lazily construct a Factory  if (factory == null) {  factory = new Factory(key, parameter, subKey, valuesMap);  }   if (supplier == null) {  supplier = valuesMap.putIfAbsent(subKey, factory);  if (supplier == null) {   // successfully installed Factory   supplier = factory;  }  // else retry with winning supplier  } else {  if (valuesMap.replace(subKey, supplier, factory)) {   // successfully replaced   // cleared CacheEntry / unsuccessful Factory   // with our Factory   supplier = factory;  } else {   // retry with current supplier   supplier = valuesMap.get(subKey);  }  }  }  }

我们可以看到它调用了 supplier.get(); 获取动态代理类,其中supplier是Factory,这个类定义在WeakCach的内部。

来瞅瞅,get里面又做了什么?

public synchronized V get() { // serialize access  // re-check  Supplier<V> supplier = valuesMap.get(subKey);  if (supplier != this) {  // something changed while we were waiting:  // might be that we were replaced by a CacheValue  // or were removed because of failure ->  // return null to signal WeakCache.get() to retry  // the loop  return null;  }  // else still us (supplier == this)   // create new value  V value = null;  try {  value = Objects.requireNonNull(valueFactory.apply(key, parameter));  } finally {  if (value == null) { // remove us on failure   valuesMap.remove(subKey, this);  }  }  // the only path to reach here is with non-null value  assert value != null;   // wrap value with CacheValue (WeakReference)  CacheValue<V> cacheValue = new CacheValue<>(value);   // try replacing us with CacheValue (this should always succeed)  if (valuesMap.replace(subKey, this, cacheValue)) {  // put also in reverseMap  reverseMap.put(cacheValue, Boolean.TRUE);  } else {  throw new AssertionError("Should not reach here");  }   // successfully replaced us with new CacheValue -> return the value  // wrapped by it  return value;  }  }

发现重点还是木有出现,但我们可以看到它调用了valueFactory.apply(key, parameter)方法:

  private static final class ProxyClassFactory  implements BiFunction<ClassLoader, Class<?>[], Class<?>>  {  // prefix for all proxy class names  private static final String proxyClassNamePrefix = "$Proxy";   // next number to use for generation of unique proxy class names  private static final AtomicLong nextUniqueNumber = new AtomicLong();   @Override  public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {   Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);  for (Class<?> intf : interfaces) {    Class<?> interfaceClass = null;  try {   interfaceClass = Class.forName(intf.getName(), false, loader);  } catch (ClassNotFoundException e) {  }  if (interfaceClass != intf) {   throw new IllegalArgumentException(   intf + " is not visible from class loader");  }    if (!interfaceClass.isInterface()) {   throw new IllegalArgumentException(   interfaceClass.getName() + " is not an interface");  }    if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {   throw new IllegalArgumentException(   "repeated interface: " + interfaceClass.getName());  }  }   String proxyPkg = null; // package to define proxy class in  int accessFlags = Modifier.PUBLIC | Modifier.FINAL;     for (Class<?> intf : interfaces) {  int flags = intf.getModifiers();  if (!Modifier.isPublic(flags)) {   accessFlags = Modifier.FINAL;   String name = intf.getName();   int n = name.lastIndexOf('.');   String pkg = ((n == -1) ? "" : name.substring(0, n + 1));   if (proxyPkg == null) {   proxyPkg = pkg;   } else if (!pkg.equals(proxyPkg)) {   throw new IllegalArgumentException(   "non-public interfaces from different packages");   }  }  }   if (proxyPkg == null) {  // if no non-public proxy interfaces, use com.sun.proxy package  proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";  }     long num = nextUniqueNumber.getAndIncrement();  String proxyName = proxyPkg + proxyClassNamePrefix + num;     byte[] proxyClassFile = ProxyGenerator.generateProxyClass(  proxyName, interfaces, accessFlags);  try {  return defineClass0(loader, proxyName,    proxyClassFile, 0, proxyClassFile.length);  } catch (ClassFormatError e) {    throw new IllegalArgumentException(e.toString());  }  }  }

通过看代码终于找到了重点:

//生成字节码 byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);

那么接下来我们也使用测试一下,使用这个方法生成的字节码是个什么样子:

package jiankunking;  import sun.misc.ProxyGenerator;  import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy;   public class DynamicProxyDemonstration {  public static void main(String[] args)  {  //代理的真实对象  Subject realSubject = new RealSubject();     InvocationHandler handler = new InvocationHandlerImpl(realSubject);   ClassLoader loader = handler.getClass().getClassLoader();  Class[] interfaces = realSubject.getClass().getInterfaces();    Subject subject = (Subject) Proxy.newProxyInstance(loader, interfaces, handler);   System.out.println("动态代理对象的类型:"+subject.getClass().getName());   String hello = subject.SayHello("jiankunking");  System.out.println(hello);  // 将生成的字节码保存到本地,  createProxyClassFile();  }  private static void createProxyClassFile(){  String name = "ProxySubject";  byte[] data = ProxyGenerator.generateProxyClass(name,new Class[]{Subject.class});  FileOutputStream out =null;  try {  out = new FileOutputStream(name+".class");  System.out.println((new File("hello")).getAbsolutePath());  out.write(data);  } catch (FileNotFoundException e) {  e.printStackTrace();  } catch (IOException e) {  e.printStackTrace();  }finally {  if(null!=out) try {  out.close();  } catch (IOException e) {  e.printStackTrace();  }  }  }  }

可以看一下这里代理对象的类型:

在Java中实现JDK动态代理的原理是什么

我们用jd-jui 工具将生成的字节码反编译:

import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.lang.reflect.UndeclaredThrowableException; import jiankunking.Subject;  public final class ProxySubject  extends Proxy  implements Subject {  private static Method m1;  private static Method m3;  private static Method m4;  private static Method m2;  private static Method m0;   public ProxySubject(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 localError)  {  throw localError;  }  catch (Throwable localThrowable)  {  throw new UndeclaredThrowableException(localThrowable);  }  }   public final String SayGoodBye()  {  try  {  return (String)this.h.invoke(this, m3, null);  }  catch (Error|RuntimeException localError)  {  throw localError;  }  catch (Throwable localThrowable)  {  throw new UndeclaredThrowableException(localThrowable);  }  }   public final String SayHello(String paramString)  {  try  {  return (String)this.h.invoke(this, m4, new Object[] { paramString });  }  catch (Error|RuntimeException localError)  {  throw localError;  }  catch (Throwable localThrowable)  {  throw new UndeclaredThrowableException(localThrowable);  }  }   public final String toString()  {  try  {  return (String)this.h.invoke(this, m2, null);  }  catch (Error|RuntimeException localError)  {  throw localError;  }  catch (Throwable localThrowable)  {  throw new UndeclaredThrowableException(localThrowable);  }  }   public final int hashCode()  {  try  {  return ((Integer)this.h.invoke(this, m0, null)).intValue();  }  catch (Error|RuntimeException localError)  {  throw localError;  }  catch (Throwable localThrowable)  {  throw new UndeclaredThrowableException(localThrowable);  }  }   static  {  try  {  m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });  m3 = Class.forName("jiankunking.Subject").getMethod("SayGoodBye", new Class[0]);  m4 = Class.forName("jiankunking.Subject").getMethod("SayHello", new Class[] { Class.forName("java.lang.String") });  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 localNoSuchMethodException)  {  throw new NoSuchMethodError(localNoSuchMethodException.getMessage());  }  catch (ClassNotFoundException localClassNotFoundException)  {  throw new NoClassDefFoundError(localClassNotFoundException.getMessage());  }  } }

这就是最终真正的代理类,它继承自Proxy并实现了我们定义的Subject接口

也就是说:

Subject subject = (Subject) Proxy.newProxyInstance(loader, interfaces, handler);

这里的subject实际是这个类的一个实例,那么我们调用它的:

public final String SayHello(String paramString)

就是调用我们定义的InvocationHandlerImpl的 invoke方法:

在Java中实现JDK动态代理的原理是什么

=======横线之间的是代码跟分析的过程,不想看的朋友可以直接看结论================

五、结论

到了这里,终于解答了:

subject.SayHello("jiankunking")这句话时,为什么会自动调用InvocationHandlerImpl的invoke方法?

因为JDK生成的最终真正的代理类,它继承自Proxy并实现了我们定义的Subject接口,在实现Subject接口方法的内部,通过反射调用了InvocationHandlerImpl的invoke方法。

包含生成本地class文件的demo:

http://xiazai.jb51.net/201707/yuanma/DynamicProxyDemo2(jb51.net).rar

通过分析代码可以看出Java 动态代理,具体有如下四步骤:

看完上述内容,你们掌握在Java中实现JDK动态代理的原理是什么的方法了吗?如果还想学到更多技能或想了解更多相关内容,欢迎关注编程网行业资讯频道,感谢各位的阅读!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     801人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     348人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     311人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     432人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     220人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯