一、什么是代理

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

代理模式UML图:

结构示意图:

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

二、Java动态代理

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

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

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

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

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

  1. //所在包
  2. package java.lang.reflect;
  3. //Proxy类的定义
  4. public class Proxy implements java.io.Serializable {}

Proxy提供了用于创建对象的静态方法,这些对象充当接口实例但允许自定义方法调用。 要为接口Foo创建代理实例:

  1. InvocationHandler handler = new MyInvocationHandler(...);
  2. Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(), new Class<?>[] { Foo.class }, handler);

1、构造函数,用于给内部的h赋值

  1. protected Proxy(InvocationHandler h)

2、获得一个代理类,其中loader是类装载器,interfaces是真实类所拥有的全部接口的数组。

  1. static Class getProxyClass (ClassLoaderloader, Class[] interfaces)

3、返回代理类的一个实例,返回后的代理类可以当作被代理类使用(可使用被代理类的在Subject接口中声明过的方法):

  1. static Object newProxyInstance(ClassLoaderloader, Class[] interfaces, InvocationHandler h)

所谓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. 通过代理调用方法

三、具体使用

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

  1. package CSDN.jdkProxy;
  2. public interface Subject {
  3. /**
  4. * @param name
  5. * @return
  6. */
  7. public String SayHello(String name);
  8. /**
  9. * @return
  10. */
  11. public String SayGoodBye();
  12. }

2、需要代理的实际对象

  1. package CSDN.jdkProxy;
  2. public class RealSubject implements Subject {
  3. /**
  4. * 你好
  5. *
  6. * @param name
  7. * @return
  8. */
  9. public String SayHello(String name)
  10. {
  11. return "hello " + name;
  12. }
  13. /**
  14. * 再见
  15. *
  16. * @return
  17. */
  18. public String SayGoodBye()
  19. {
  20. return " good bye ";
  21. }
  22. }

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

  1. package CSDN.jdkProxy;
  2. import java.lang.reflect.InvocationHandler;
  3. import java.lang.reflect.Method;
  4. public class InvocationHandlerImpl implements InvocationHandler {
  5. /**
  6. * 这个就是我们要代理的真实对象
  7. */
  8. private Object subject;
  9. /**
  10. * 构造方法,给我们要代理的真实对象赋初值
  11. *
  12. * @param subject
  13. */
  14. public InvocationHandlerImpl(Object subject)
  15. {
  16. this.subject = subject;
  17. }
  18. /**
  19. * 该方法负责集中处理动态代理类上的所有方法调用。
  20. * 调用处理器根据这三个参数进行预处理或分派到委托类实例上反射执行
  21. *
  22. * @param proxy 代理类实例
  23. * @param method 被调用的方法对象
  24. * @param args 调用参数
  25. * @return
  26. * @throws Throwable
  27. */
  28. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  29. //在代理真实对象前我们可以添加一些自己的操作
  30. System.out.println("在调用之前,我要干点啥呢?");
  31. System.out.println("Method:" + method);
  32. //当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用
  33. Object returnValue = method.invoke(subject, args);
  34. //在代理真实对象后我们也可以添加一些自己的操作
  35. System.out.println("在调用之后,我要干点啥呢?");
  36. return returnValue;
  37. }
  38. }

4、测试

  1. package CSDN.jdkProxy;
  2. import java.lang.reflect.InvocationHandler;
  3. import java.lang.reflect.Proxy;
  4. public class DynamicProxyDemonstration {
  5. public static void main(String[] args) {
  6. //代理的真实对象
  7. Subject realSubject = new RealSubject();
  8. /**
  9. * InvocationHandlerImpl 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发
  10. * 其内部通常包含指向委托类实例的引用,用于真正执行分派转发过来的方法调用.
  11. * 即:要代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调用其方法
  12. */
  13. InvocationHandler handler = new InvocationHandlerImpl(realSubject);
  14. ClassLoader loader = realSubject.getClass().getClassLoader();
  15. Class[] interfaces = realSubject.getClass().getInterfaces();
  16. /**
  17. * 该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例
  18. */
  19. Subject subject = (Subject) Proxy.newProxyInstance(loader, interfaces, handler);
  20. System.out.println("动态代理对象的类型:"+subject.getClass().getName());
  21. String hello = subject.SayHello("jiankunking");
  22. System.out.println(hello);
  23. String goodbye = subject.SayGoodBye();
  24. System.out.println(goodbye);
  25. }
  26. }

5、结果输出

  1. 动态代理对象的类型:com.sun.proxy.$Proxy0
  2. 在调用之前,我要干点啥呢?
  3. Method:public abstract java.lang.String CSDN.jdkProxy.Subject.SayHello(java.lang.String)
  4. 在调用之后,我要干点啥呢?
  5. hello jiankunking
  6. 在调用之前,我要干点啥呢?
  7. Method:public abstract java.lang.String CSDN.jdkProxy.Subject.SayGoodBye()
  8. 在调用之后,我要干点啥呢?
  9. good bye

四、动态代理实现原理

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

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

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

先看看newProxyInstance( )方法源码:

  1. public static Object newProxyInstance(ClassLoader loader,
  2. Class<?>[] interfaces,
  3. InvocationHandler h)
  4. throws IllegalArgumentException{
  5. //参数 h 的非空判断
  6. Objects.requireNonNull(h);
  7. final Class<?>[] intfs = interfaces.clone();
  8. final SecurityManager sm = System.getSecurityManager();
  9. if (sm != null) {
  10. checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
  11. }
  12. /*
  13. * Look up or generate the designated proxy class.
  14. * 获得(查找或者实现)与指定类装载器和一组接口相关的代理类类型的对象,也就是Class对象
  15. */
  16. Class<?> cl = getProxyClass0(loader, intfs);
  17. /*
  18. * Invoke its constructor with the designated invocation handler.
  19. * 通过反射获取构造函数对象并生成代理类实例
  20. */
  21. try {
  22. if (sm != null) {
  23. checkNewProxyPermission(Reflection.getCallerClass(), cl);
  24. }
  25. //获取代理对象的构造方法(也就是$Proxy0(InvocationHandler h))
  26. final Constructor<?> cons = cl.getConstructor(constructorParams);
  27. final InvocationHandler ih = h;
  28. if (!Modifier.isPublic(cl.getModifiers())) {
  29. AccessController.doPrivileged(new PrivilegedAction<Void>() {
  30. public Void run() {
  31. cons.setAccessible(true);
  32. return null;
  33. }
  34. });
  35. }
  36. //生成代理类的实例并把InvocationHandlerImpl的实例传给它的构造方法
  37. return cons.newInstance(new Object[]{h});
  38. } catch (IllegalAccessException|InstantiationException e) {
  39. throw new InternalError(e.toString(), e);
  40. } catch (InvocationTargetException e) {
  41. Throwable t = e.getCause();
  42. if (t instanceof RuntimeException) {
  43. throw (RuntimeException) t;
  44. } else {
  45. throw new InternalError(t.toString(), t);
  46. }
  47. } catch (NoSuchMethodException e) {
  48. throw new InternalError(e.toString(), e);
  49. }
  50. }

再看看getProxyClass0( )方法源码:

  1. /**
  2. * Generate a proxy class. Must call the checkProxyAccess method
  3. * to perform permission checks before calling this.
  4. * 生成一个代理类。但是在调用getProxyClass0()方法前必须先调用checkProxyAccess()方法进行权限检查
  5. */
  6. private static Class<?> getProxyClass0(ClassLoader loader,
  7. Class<?>... interfaces) {
  8. if (interfaces.length > 65535) {
  9. throw new IllegalArgumentException("interface limit exceeded");
  10. }
  11. // If the proxy class defined by the given loader implementing
  12. // the given interfaces exists, this will simply return the cached copy;
  13. // otherwise, it will create the proxy class via the ProxyClassFactory
  14. // 如果通过给定的接口的loader已经实现了代理类,则只返回缓存副本;否则,它将通过ProxyClassFactory创建代理类
  15. return proxyClassCache.get(loader, interfaces);
  16. }

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

  1. /**
  2. * a cache of proxy classes
  3. */
  4. private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
  5. proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

原来用了一下缓存啊,那么它对应的get方法啥样呢?

  1. /**
  2. * Look-up the value through the cache. This always evaluates the
  3. * {@code subKeyFactory} function and optionally evaluates
  4. * {@code valueFactory} function if there is no entry in the cache for given
  5. * pair of (key, subKey) or the entry has already been cleared.
  6. *
  7. * @param key possibly null key
  8. * @param parameter parameter used together with key to create sub-key and
  9. * value (should not be null)
  10. * @return the cached value (never null)
  11. * @throws NullPointerException if {@code parameter} passed in or
  12. * {@code sub-key} calculated by
  13. * {@code subKeyFactory} or {@code value}
  14. * calculated by {@code valueFactory} is null.
  15. */
  16. public V get(K key, P parameter) {
  17. Objects.requireNonNull(parameter);
  18. expungeStaleEntries();
  19. Object cacheKey = CacheKey.valueOf(key, refQueue);
  20. // lazily install the 2nd level valuesMap for the particular cacheKey
  21. ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
  22. if (valuesMap == null) {
  23. //putIfAbsent这个方法在key不存在的时候加入一个值,如果key存在就不放入
  24. ConcurrentMap<Object, Supplier<V>> oldValuesMap
  25. = map.putIfAbsent(cacheKey,
  26. valuesMap = new ConcurrentHashMap<>());
  27. if (oldValuesMap != null) {
  28. valuesMap = oldValuesMap;
  29. }
  30. }
  31. // create subKey and retrieve the possible Supplier<V> stored by that
  32. // subKey from valuesMap
  33. Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
  34. Supplier<V> supplier = valuesMap.get(subKey);
  35. Factory factory = null;
  36. while (true) {
  37. if (supplier != null) {
  38. // supplier might be a Factory or a CacheValue<V> instance
  39. V value = supplier.get();
  40. if (value != null) {
  41. return value;
  42. }
  43. }
  44. // else no supplier in cache
  45. // or a supplier that returned null (could be a cleared CacheValue
  46. // or a Factory that wasn't successful in installing the CacheValue)
  47. // lazily construct a Factory
  48. if (factory == null) {
  49. factory = new Factory(key, parameter, subKey, valuesMap);
  50. }
  51. if (supplier == null) {
  52. supplier = valuesMap.putIfAbsent(subKey, factory);
  53. if (supplier == null) {
  54. // successfully installed Factory
  55. supplier = factory;
  56. }
  57. // else retry with winning supplier
  58. } else {
  59. if (valuesMap.replace(subKey, supplier, factory)) {
  60. // successfully replaced
  61. // cleared CacheEntry / unsuccessful Factory
  62. // with our Factory
  63. supplier = factory;
  64. } else {
  65. // retry with current supplier
  66. supplier = valuesMap.get(subKey);
  67. }
  68. }
  69. }
  70. }

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

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

  1. public synchronized V get() {
  2. // serialize access
  3. // re-check
  4. Supplier<V> supplier = valuesMap.get(subKey);
  5. if (supplier != this) {
  6. // something changed while we were waiting:
  7. // might be that we were replaced by a CacheValue
  8. // or were removed because of failure ->
  9. // return null to signal WeakCache.get() to retry
  10. // the loop
  11. return null;
  12. }
  13. // else still us (supplier == this)
  14. // create new value
  15. V value = null;
  16. try {
  17. value = Objects.requireNonNull(valueFactory.apply(key, parameter));
  18. } finally {
  19. if (value == null) { // remove us on failure
  20. valuesMap.remove(subKey, this);
  21. }
  22. }
  23. // the only path to reach here is with non-null value
  24. assert value != null;
  25. // wrap value with CacheValue (WeakReference)
  26. CacheValue<V> cacheValue = new CacheValue<>(value);
  27. // try replacing us with CacheValue (this should always succeed)
  28. if (valuesMap.replace(subKey, this, cacheValue)) {
  29. // put also in reverseMap
  30. reverseMap.put(cacheValue, Boolean.TRUE);
  31. } else {
  32. throw new AssertionError("Should not reach here");
  33. }
  34. // successfully replaced us with new CacheValue -> return the value
  35. // wrapped by it
  36. return value;
  37. }
  38. }

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

  1. /**
  2. * A factory function that generates, defines and returns the proxy class given
  3. * the ClassLoader and array of interfaces.
  4. */
  5. private static final class ProxyClassFactory
  6. implements BiFunction<ClassLoader, Class<?>[], Class<?>>
  7. {
  8. // prefix for all proxy class names
  9. private static final String proxyClassNamePrefix = "$Proxy";
  10. // next number to use for generation of unique proxy class names
  11. private static final AtomicLong nextUniqueNumber = new AtomicLong();
  12. @Override
  13. public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
  14. Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
  15. for (Class<?> intf : interfaces) {
  16. /*
  17. * Verify that the class loader resolves the name of this
  18. * interface to the same Class object.
  19. */
  20. Class<?> interfaceClass = null;
  21. try {
  22. interfaceClass = Class.forName(intf.getName(), false, loader);
  23. } catch (ClassNotFoundException e) {
  24. }
  25. if (interfaceClass != intf) {
  26. throw new IllegalArgumentException(
  27. intf + " is not visible from class loader");
  28. }
  29. /*
  30. * Verify that the Class object actually represents an
  31. * interface.
  32. */
  33. if (!interfaceClass.isInterface()) {
  34. throw new IllegalArgumentException(
  35. interfaceClass.getName() + " is not an interface");
  36. }
  37. /*
  38. * Verify that this interface is not a duplicate.
  39. */
  40. if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
  41. throw new IllegalArgumentException(
  42. "repeated interface: " + interfaceClass.getName());
  43. }
  44. }
  45. String proxyPkg = null; // package to define proxy class in
  46. int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
  47. /*
  48. * Record the package of a non-public proxy interface so that the
  49. * proxy class will be defined in the same package. Verify that
  50. * all non-public proxy interfaces are in the same package.
  51. */
  52. for (Class<?> intf : interfaces) {
  53. int flags = intf.getModifiers();
  54. if (!Modifier.isPublic(flags)) {
  55. accessFlags = Modifier.FINAL;
  56. String name = intf.getName();
  57. int n = name.lastIndexOf('.');
  58. String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
  59. if (proxyPkg == null) {
  60. proxyPkg = pkg;
  61. } else if (!pkg.equals(proxyPkg)) {
  62. throw new IllegalArgumentException("non-public interfaces from different packages");
  63. }
  64. }
  65. }
  66. if (proxyPkg == null) {
  67. // if no non-public proxy interfaces, use com.sun.proxy package
  68. proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
  69. }
  70. /*
  71. * Choose a name for the proxy class to generate.
  72. */
  73. long num = nextUniqueNumber.getAndIncrement();
  74. String proxyName = proxyPkg + proxyClassNamePrefix + num;
  75. /*
  76. * Generate the specified proxy class.
  77. */
  78. byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);
  79. try {
  80. return defineClass0(loader, proxyName,
  81. proxyClassFile, 0, proxyClassFile.length);
  82. } catch (ClassFormatError e) {
  83. /*
  84. * A ClassFormatError here means that (barring bugs in the
  85. * proxy class generation code) there was some other
  86. * invalid aspect of the arguments supplied to the proxy
  87. * class creation (such as virtual machine limitations
  88. * exceeded).
  89. */
  90. throw new IllegalArgumentException(e.toString());
  91. }
  92. }
  93. }

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

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

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

  1. import sun.misc.ProxyGenerator;
  2. import java.io.File;
  3. import java.io.FileNotFoundException;
  4. import java.io.FileOutputStream;
  5. import java.io.IOException;
  6. import java.lang.reflect.InvocationHandler;
  7. import java.lang.reflect.Proxy;
  8. /**
  9. * 动态代理演示
  10. */
  11. public class DynamicProxyDemonstration
  12. {
  13. public static void main(String[] args)
  14. {
  15. //代理的真实对象
  16. Subject realSubject = new RealSubject();
  17. /**
  18. * InvocationHandlerImpl 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发
  19. * 其内部通常包含指向委托类实例的引用,用于真正执行分派转发过来的方法调用.
  20. * 即:要代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调用其方法
  21. */
  22. InvocationHandler handler = new InvocationHandlerImpl(realSubject);
  23. ClassLoader loader = handler.getClass().getClassLoader();
  24. Class[] interfaces = realSubject.getClass().getInterfaces();
  25. /**
  26. * 该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例
  27. */
  28. Subject subject = (Subject) Proxy.newProxyInstance(loader, interfaces, handler);
  29. System.out.println("动态代理对象的类型:"+subject.getClass().getName());
  30. String hello = subject.SayHello("jiankunking");
  31. System.out.println(hello);
  32. // 将生成的字节码保存到本地,
  33. createProxyClassFile();
  34. }
  35. private static void createProxyClassFile(){
  36. String name = "ProxySubject";
  37. byte[] data = ProxyGenerator.generateProxyClass(name,new Class[]{Subject.class});
  38. FileOutputStream out =null;
  39. try {
  40. out = new FileOutputStream(name+".class");
  41. System.out.println((new File("hello")).getAbsolutePath());
  42. out.write(data);
  43. } catch (FileNotFoundException e) {
  44. e.printStackTrace();
  45. } catch (IOException e) {
  46. e.printStackTrace();
  47. }finally {
  48. if(null!=out) try {
  49. out.close();
  50. } catch (IOException e) {
  51. e.printStackTrace();
  52. }
  53. }
  54. }
  55. }

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

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

  1. import java.lang.reflect.InvocationHandler;
  2. import java.lang.reflect.Method;
  3. import java.lang.reflect.Proxy;
  4. import java.lang.reflect.UndeclaredThrowableException;
  5. import jiankunking.Subject;
  6. public final class ProxySubject
  7. extends Proxy
  8. implements Subject {
  9. private static Method m1;
  10. private static Method m3;
  11. private static Method m4;
  12. private static Method m2;
  13. private static Method m0;
  14. public ProxySubject(InvocationHandler paramInvocationHandler)
  15. {
  16. super(paramInvocationHandler);
  17. }
  18. public final boolean equals(Object paramObject)
  19. {
  20. try {
  21. return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
  22. }
  23. catch (Error|RuntimeException localError)
  24. {
  25. throw localError;
  26. }
  27. catch (Throwable localThrowable)
  28. {
  29. throw new UndeclaredThrowableException(localThrowable);
  30. }
  31. }
  32. public final String SayGoodBye()
  33. {
  34. try
  35. {
  36. return (String)this.h.invoke(this, m3, null);
  37. }
  38. catch (Error|RuntimeException localError)
  39. {
  40. throw localError;
  41. }
  42. catch (Throwable localThrowable)
  43. {
  44. throw new UndeclaredThrowableException(localThrowable);
  45. }
  46. }
  47. public final String SayHello(String paramString)
  48. {
  49. try
  50. {
  51. return (String)this.h.invoke(this, m4, new Object[] { paramString });
  52. }
  53. catch (Error|RuntimeException localError)
  54. {
  55. throw localError;
  56. }
  57. catch (Throwable localThrowable)
  58. {
  59. throw new UndeclaredThrowableException(localThrowable);
  60. }
  61. }
  62. public final String toString()
  63. {
  64. try
  65. {
  66. return (String)this.h.invoke(this, m2, null);
  67. }
  68. catch (Error|RuntimeException localError)
  69. {
  70. throw localError;
  71. }
  72. catch (Throwable localThrowable)
  73. {
  74. throw new UndeclaredThrowableException(localThrowable);
  75. }
  76. }
  77. public final int hashCode()
  78. {
  79. try
  80. {
  81. return ((Integer)this.h.invoke(this, m0, null)).intValue();
  82. }
  83. catch (Error|RuntimeException localError)
  84. {
  85. throw localError;
  86. }
  87. catch (Throwable localThrowable)
  88. {
  89. throw new UndeclaredThrowableException(localThrowable);
  90. }
  91. }
  92. static
  93. {
  94. try
  95. {
  96. m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
  97. m3 = Class.forName("jiankunking.Subject").getMethod("SayGoodBye", new Class[0]);
  98. m4 = Class.forName("jiankunking.Subject").getMethod("SayHello", new Class[] { Class.forName("java.lang.String") });
  99. m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
  100. m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
  101. return;
  102. }
  103. catch (NoSuchMethodException localNoSuchMethodException)
  104. {
  105. throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
  106. }
  107. catch (ClassNotFoundException localClassNotFoundException)
  108. {
  109. throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
  110. }
  111. }
  112. }

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

也就是说:

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

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

  1. public final String SayHello(String paramString)

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

五、结论

到了这里,终于解答了:

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

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

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

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

最后

感谢你看到这里,文章有什么不足还请指正,觉得文章对你有帮助的话记得给我点个赞,每天都会分享java相关技术文章或行业资讯,欢迎大家关注和转发文章!

金九银十想面BAT?那这些JDK 动态代理的面试点你一定要知道的更多相关文章

  1. 金九银十想去跳槽面试?那这份Java面经你真得看看了,写的非常详细!

    前言 前两天在和朋友吃饭的时候聊到时间这个东西是真的过的好坏啊,金三银四仿佛还在昨天.一眨眼金九银十又快到了,对程序员来说这两个是一年最合适的跳槽涨薪环节了,今年的你已经做好准备了吗?不妨看看这篇文章 ...

  2. 备战金九银十,Java研发面试题(Spring、MySQL、JVM、Mybatis、Redis、Tomcat)[带答案],刷起来!

    八月在即,马上就是"金九银十",又是跳槽招聘季.咱们这行公认涨薪不如跳槽加的快.但不建议频繁跳槽,还是要学会融合团队,抓住每个机会提升技能. 苏先生在这里给大家整理了一套各大互联网 ...

  3. 不等"金九银十",金风八月,我早已拿下字节跳动的offer

    字节跳动,我是在网上投的简历,之前也投过一次,简历都没通过删选,后来让师姐帮我改了一下简历,重新投另一个部门,获得了面试机会.7月23日,中午HR打电话过来预约了下午4点半面试,说会在线写代码,让我准 ...

  4. “金九银十”已过,总结我的天猫、蚂蚁、头条面试经历(Java岗)

    跳槽时时刻刻都在发生,但是我建议大家跳槽之前,先想清楚为什么要跳槽.切不可跟风,看到同事一个个都走了,自己也盲目的开始面试起来(期间也没有准备充分),到底是因为技术原因(影响自己的发展,偏移自己规划的 ...

  5. 金九银十跳槽高峰,面试必备之 Redis + MongoDB 常问80道面试题

    前言 有着“金九银十”之称的招聘旺季已经开启,跳槽高峰期也如约而至. 本文为主要是 Redis + MongoDB 知识点的攻略,希望能帮助到大家. 内容较多,大家准备好耐心和瓜子矿泉水. Redis ...

  6. 金九银十已到!掌握这300道java高频面试题,助你面试BAT无忧!

    前言 不知不觉已经到了九月了,回首看年初的时候简直像做梦一样.不得不说时间真的是无情一般的流逝,题外话就不多说了!回归正题,现在已经到了今年最后一波大好的跳槽涨薪的时机了,错过了这一次可能你就得等到明 ...

  7. 两年经验拿到蚂蚁金服,字节offer,附上金九银十BAT面试核心知识点整理

    前言 我自己是本科毕业后在老东家干了两年多,老东家算是一家"小公司"(毕竟这年头没有 BAT 或 TMD 的 title 都不好意思报出身),毕业这两年多我也没有在大厂待过,因此找 ...

  8. 想要金九银十面试通关,不懂 Java多线程肯定是不行的!

    作者 | 纳达丶无忌 如果对什么是线程.什么是进程仍存有疑惑,请先 Google 之,因为这两个概念不在本文的范围之内. 用多线程只有一个目的,那就是更好的利用 CPU 的资源,因为所有的多线程代码都 ...

  9. Android&Java面试题大全—金九银十面试必备

    声明本文由作者:Man不经心授权转载,转载请联系原文作者原文链接:https://www.jianshu.com/p/375ad14096b3, 类加载过程 Java 中类加载分为 3 个步骤:加载. ...

随机推荐

  1. Markdown基础知识

    一 Markdown简介 Markdown是⼀种可以使⽤普通⽂本编辑器编写的标记语⾔,通过简单的标记语法,它可以使普通⽂本内容具有⼀定的格式,可以简单理解为纯⽂本格式的word. 软件⼀般⽤vscod ...

  2. Linux显示系统信息sh脚本

    #!/bin/bash # #******************************************************************** #Author: wangxia ...

  3. 通过两行代码即可调整苹果电脑 Launchpad 图标大小

    之前用 13 寸 Mac 的时候我还没觉得,后来换了 16 寸就发现有点不对劲了.因为 Mac 的高分辨率,当你进入 Launchpad 界面,应用图标的大小可能会让你怀疑:这特么是苹果的设计吗?有点 ...

  4. Linux常用操作命令大全

    0.新建操作:1.查看操作    2.删除操作 3.复制操作    4.移动操作:5.重命名操作: 6.解压压缩操作    7.上传文件工具    8.ln.file和touch命令 9.查找操作命令 ...

  5. sql server DDL语句 建立数据库 定义表 修改字段等

    一.数据库:1.建立数据库 create database 数据库名;use 数据库名; create database exp1;use exp1; mysql同样 2.删除数据库 drop dat ...

  6. JavaScript动态显示时间

    <body> <div></div> <script> var div = document.querySelector('div'); retNowT ...

  7. 数据结构(C++)——顺序表

    顺序表和链表的比较 1.存取方式 顺序表可以随机访问,而链表只能从表头顺序查找.(因此经常查找顺序表某一个元素时,顺序表更适合) 2.逻辑结构与物理结构 顺序表中,逻辑上相邻的元素,其物理存储位置也相 ...

  8. 微信小程序UI自动化: minium文档部署

    目录 参考资料 1. 在线文档(临时) 2. 本地部署 参考资料 https://git.weixin.qq.com/minitest/minium-doc 1. 在线文档(临时) 其实上面的链接里面 ...

  9. 4G DTU的通信距离是多少

    4G是现今应用非常广泛的一种通信技术,主要是为广义的远程信息处理提供服务.随着计算机与各种具有处理功能的智能设备在各领域的日益广泛使用,数据通信的应用范围也日益扩大.在物联网领域中,4G是移动设备实现 ...

  10. go-zero 是如何追踪你的请求链路的

    go-zero 是如何追踪你的请求链路 微服务架构中,调用链可能很漫长,从 http 到 rpc ,又从 rpc 到 http .而开发者想了解每个环节的调用情况及性能,最佳方案就是 全链路跟踪. 追 ...