栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Java JDK动态代理(AOP)的实现原理与使用详析

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Java JDK动态代理(AOP)的实现原理与使用详析

本文主要给大家介绍了关于Java JDK动态代理(AOP)实现原理与使用的相关内容,分享出来供大家参考学习,下面来一起看看详细的介绍:

一、什么是代理?

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

代理模式UML图:

简单结构示意图:

为了保持行为的一致性,代理类和委托类通常会实现相同的接口,所以在访问者看来两者没有丝毫的区别。通过代理类这中间一层,能有效控制对委托类对象的直接访问,也可以很好地隐藏和保护委托类对象,同时也为实施不同控制策略预留了空间,从而在设计上获得了更大的灵活性。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() { 
  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[], 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> valuesMap = map.get(cacheKey); 
 if (valuesMap == null) { 
 //putIfAbsent这个方法在key不存在的时候加入一个值,如果key存在就不放入 
 ConcurrentMap> oldValuesMap 
 = map.putIfAbsent(cacheKey, 
   valuesMap = new ConcurrentHashMap<>()); 
 if (oldValuesMap != null) { 
 valuesMap = oldValuesMap; 
 } 
 } 
 
 // create subKey and retrieve the possible Supplier stored by that 
 // subKey from valuesMap 
 Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter)); 
 Supplier supplier = valuesMap.get(subKey); 
 Factory factory = null; 
 
 while (true) { 
 if (supplier != null) { 
 // supplier might be a Factory or a Cachevalue 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 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 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[], 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, 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(); 
 } 
 } 
 } 
 
} 

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

我们用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方法:

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

五、结论

到了这里,终于解答了:

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

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

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

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

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

  • 通过实现 InvocationHandler 接口创建自己的调用处理器;
  • 通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类;
  • 通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;
  • 通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。

本文参考过:

https://www.jb51.net/kf/201608/533663.html

http://www.ibm.com/developerworks/cn/java/j-lo-proxy1/index.html

好了,以上就是这篇文章的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对考高分网的支持。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/145245.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号