Blog.csdn.net/u010061691/…

Java JDK dynamic proxies are dynamically generated bytecode and reflection mechanism of a union, said many people are used to reflect the reflection mechanism, as long as get the corresponding Class Class object to call methods, access to the member variable and so on, then the JDK dynamic proxy is in the process of the program is run by the dynamic inspection of our maintenance code, On normal business code before, so how I call my normal business code, since the business may have a lot of kinds, that is to say, may have different classes, but have to perform the same check code, if we want to take Money, or modify the ID card, must be verified by can, this is obviously the two classes, one is Money, one is the ID, So when we call normal business code, we don’t know whose code we are calling, so we use reflection, by reflection, dynamically identify the type, and then call the method, if we want to get Money, then we should pass in an instance of Money, through this instance getDeclaredMethod, Or getMethod, get the method of the instance, and then you can call the method dynamically, just by adding the code that we’ve verified before you use reflection, which is how reflection applies to dynamic proxies. This part of the code requires us to implement our own InvocationHandler interface and implement the Invoke method, which in this case is our reflection implementation. Of course, in order to call the method of the corresponding class, we implement the invocationHandler class, which needs to hold the instance of the class we want to delegate.

Dynamic generation of bytecodes is a technique in which the type of bytecodes to be generated cannot be determined at compile time, that is, there is no corresponding Java file, so class files cannot be generated. For static proxy, we explicitly implement a proxy class, so bytecodes can be generated at compile time. But a dynamic proxy can’t clear to realize a certain kind of proxy class, is for all the business of a common class, because can’t decided to generate the business class at compile-time proxy class so will not be able to generate the bytecode, but is at run time, see our incoming what class instance is input, generate the corresponding class proxy class, If there is no bytecode file, then the class will not be loaded, let alone executed. Therefore, dynamic proxy technology will dynamically splice the bytecode file into a class file, which is a dynamically generated bytecode file. This technique is implemented in a Java class

Proxy static method, newProxyInstance (), which takes three arguments. The first argument is the classLoader of the proxied class. The second argument is all the interfaces of the proxied class. The third argument is an instance of our implementation class above (InvocationHandler), so this method returns a proxy class that dynamically concatenates the bytecode of the proxy class in this method.

Dynamic proxies have two main applications: The first is that if more than the code will use the same code, such as security checks and permissions validation, in is on the business module is used, typically on our business code in the head or the tail, so if it’s not dynamic proxy, the same code will be dispersed in each part of your program, the first use of the code is, the second is the maintainability is not high, If you want to change the way you access authentication, so you need to change every place in the program, it will be difficult to maintain, altogether we will all check code, are extracted, maintenance, together with such code maintenance together, then it is quite convenient, this is the aspect oriented programming is AOP, We maintain the inspection code together in a class called facets. AOP concepts are widely used in Spring, such as life transactions, which are implemented using AOP to dynamically add transactions to database operations. The second major application is an application similar to interceptors. Interceptors are applications that add checking code in front of your business code and execute your business code if they pass. If not, your business code will not be executed. Here is not fully use dynamic proxy, mainly with dynamic proxies in the reflection of the implementation, in the actual applications typically use the interceptor stack, which is a series of interceptor, means more legitimacy examination, an interceptor validation through into the next interceptor, when all interceptor verification through, is the real business code. This idea is in fact the chain of responsibility pattern in the design pattern, will have a great application in SpringMVC, below I simple manual implementation of an interceptor (not interceptor stack), this is both an interceptor, but also a dynamic proxy application, I hope to help you, here is my code.



I’ve been using JDK dynamic proxies before, but I haven’t been able to figure it out. For example, who calls the Invoke method of InvocationHandler, and how the proxy object is generated, have not been fully understood until the last few weeks.

Without further ado, let’s take a look at how the JDK dynamic is used.

Java code

  1. package dynamic.proxy;
  2. import java.lang.reflect.InvocationHandler;
  3. import java.lang.reflect.Method;
  4. import java.lang.reflect.Proxy;
  5. / * *
  6. * Implement your own InvocationHandler
  7. * @author zyb
  8. * @since 2012-8-9
  9. *
  10. * /
  11. public class MyInvocationHandler implements InvocationHandler {
  12. // Target object
  13. private Object target;
  14. / * *
  15. * Construction method
  16. * @param target Target object
  17. * /
  18. public MyInvocationHandler(Object target) {
  19. super();
  20. this.target = target;
  21. }
  22. / * *
  23. * The method that executes the target object
  24. * /
  25. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  26. // Simply print the target object’s method before executing it
  27. System.out.println(“——————before——————“);
  28. // Execute the target object’s method
  29. Object result = method.invoke(target, args);
  30. // Simply print after the target object’s method is executed
  31. System.out.println(“——————-after——————“);
  32. return result;
  33. }
  34. / * *
  35. * Gets the proxy object of the target object
  36. * @return Proxy object
  37. * /
  38. public Object getProxy() {
  39. return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
  40. target.getClass().getInterfaces(), this);
  41. }
  42. }
  43. package dynamic.proxy;
  44. / * *
  45. * Target object implementation interface, using JDK to generate proxy objects must implement an interface
  46. * @author zyb
  47. * @since 2012-8-9
  48. *
  49. * /
  50. public interface UserService {
  51. / * *
  52. * Target method
  53. * /
  54. public abstract void add();
  55. }
  56. package dynamic.proxy;
  57. / * *
  58. * Target object
  59. * @author zyb
  60. * @since 2012-8-9
  61. *
  62. * /
  63. public class UserServiceImpl implements UserService {
  64. /* (non-Javadoc)
  65. * @see dynamic.proxy.UserService#add()
  66. * /
  67. public void add() {
  68. System.out.println(“——————–add—————“);
  69. }
  70. }
  71. package dynamic.proxy;
  72. import org.junit.Test;
  73. / * *
  74. * Dynamic proxy test classes
  75. * @author zyb
  76. * @since 2012-8-9
  77. *
  78. * /
  79. public class ProxyTest {
  80. @Test
  81. public void testProxy() throws Throwable {
  82. // Instantiate the target object
  83. UserService userService = new UserServiceImpl();
  84. // Instantiate InvocationHandler
  85. MyInvocationHandler invocationHandler = new MyInvocationHandler(userService);
  86. // Generate a proxy object based on the target object
  87. UserService proxy = (UserService) invocationHandler.getProxy();
  88. // Call the method of the proxy object
  89. proxy.add();
  90. }
  91. }

The result is as follows: ——————before—————— ——————–add————— — — — — — — — — — — — — — — — — — — — after — — — — — — — — — — — — — — — — — –, it is very simple to use, actually this is basically a simple implementation of AOP, the methods perform before and after the target object has been enhanced. Spring’s AOP implementation actually uses Proxy and InvocationHandler. It’s easy to use, but it would be nice to know what’s going on behind it. Let’s start by looking at how the JDK generates proxy objects. Since the Proxy object is generated using the static newProxyInstance of the Proxy class, let’s go to its source code and see what it does.

Java code

  1. / * *
  2. * loader: class loader
  3. * interfaces: Interfaces implemented by the target object
  4. * h: Implementation class of InvocationHandler
  5. * /
  6. public static Object newProxyInstance(ClassLoader loader,
  7. Class
    [] interfaces,
  8. InvocationHandler h)
  9. throws IllegalArgumentException
  10. {
  11. if (h == null) {
  12. throw new NullPointerException();
  13. }
  14. / *
  15. * Look up or generate the designated proxy class.
  16. * /
  17. Class cl = getProxyClass(loader, interfaces);
  18. / *
  19. * Invoke its constructor with the designated invocation handler.
  20. * /
  21. try {
  22. // Call the constructor of the proxy object (i.e. $Proxy0(InvocationHandler h))
  23. Constructor cons = cl.getConstructor(constructorParams);
  24. // Generate an instance of the proxy class and pass an instance of MyInvocationHandler to its constructor
  25. return (Object) cons.newInstance(new Object[] { h });
  26. } catch (NoSuchMethodException e) {
  27. throw new InternalError(e.toString());
  28. } catch (IllegalAccessException e) {
  29. throw new InternalError(e.toString());
  30. } catch (InstantiationException e) {
  31. throw new InternalError(e.toString());
  32. } catch (InvocationTargetException e) {
  33. throw new InternalError(e.toString());
  34. }
  35. }

Let’s go back to the getProxyClass method

Java code

  1. public static Class
    getProxyClass(ClassLoader loader,
  2. Class
    … interfaces)
  3. throws IllegalArgumentException
  4. {
  5. // Throw an exception if the target class implements more than 65535 interfaces.
  6. if (interfaces.length > 65535) {
  7. throw new IllegalArgumentException(“interface limit exceeded”);
  8. }
  9. // Declare the Class object represented by the proxy object.
  10. Class proxyClass = null;
  11. String[] interfaceNames = new String[interfaces.length];
  12. Set interfaceSet = new HashSet(); // for detecting duplicates
  13. // Iterate over the interface implemented by the target class
  14. for (int i = 0; i < interfaces.length; i++) {
  15. // Get the name of the interface implemented by the target class
  16. String interfaceName = interfaces[i].getName();
  17. Class interfaceClass = null;
  18. try {
  19. // Load the interface implemented by the target class into memory
  20. interfaceClass = Class.forName(interfaceName, false, loader);
  21. } catch (ClassNotFoundException e) {
  22. }
  23. if (interfaceClass ! = interfaces[i]) {
  24. throw new IllegalArgumentException(
  25. interfaces[i] + ” is not visible from class loader”);
  26. }
  27. // Omit some irrelevant code…….
  28. // Put the Class object represented by the interface implemented by the target Class into the Set
  29. interfaceSet.add(interfaceClass);
  30. interfaceNames[i] = interfaceName;
  31. }
  32. // Use the name of the interface implemented by the target class as the key in the cache (Map)
  33. Object key = Arrays.asList(interfaceNames);
  34. Map cache;
  35. synchronized (loaderToCache) {
  36. // Fetch the cache from the cache
  37. cache = (Map) loaderToCache.get(loader);
  38. if (cache == null) {
  39. // If not, create another HashMap instance
  40. cache = new HashMap();
  41. // Put the HashMap instance and the current loader in the cache
  42. loaderToCache.put(loader, cache);
  43. }
  44. }
  45. synchronized (cache) {
  46. do {
  47. // Retrieve objects from the cache based on the name of the interface
  48. Object value = cache.get(key);
  49. if (value instanceof Reference) {
  50. proxyClass = (Class) ((Reference) value).get();
  51. }
  52. if (proxyClass ! = null) {
  53. // If the Class instance of the proxy object already exists, it is returned directly
  54. return proxyClass;
  55. } else if (value == pendingGenerationMarker) {
  56. try {
  57. cache.wait();
  58. } catch (InterruptedException e) {
  59. }
  60. continue;
  61. } else {
  62. cache.put(key, pendingGenerationMarker);
  63. break;
  64. }
  65. } while (true);
  66. }
  67. try {
  68. // omit some code…….
  69. // This is the key to dynamically generating proxy objects
  70. byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
  71. proxyName, interfaces);
  72. try {
  73. // Generate an instance of the proxy class based on its bytecode
  74. proxyClass = defineClass0(loader, proxyName,
  75. proxyClassFile, 0, proxyClassFile.length);
  76. } catch (ClassFormatError e) {
  77. throw new IllegalArgumentException(e.toString());
  78. }
  79. }
  80. // add to set of all generated proxy classes, for isProxyClass
  81. proxyClasses.put(proxyClass, null);
  82. }
  83. // omit some code…….
  84. return proxyClass;
  85. }

Go to the static method generateProxyClass of the ProxyGenerator class and this is where the bytecode of the proxy class is actually generated.

Java code

  1. public static byte[] generateProxyClass(final String name,
  2. Class[] interfaces)
  3. {
  4. ProxyGenerator gen = new ProxyGenerator(name, interfaces);
  5. // Generate the bytecode of the proxy class dynamically
  6. final byte[] classFile = gen.generateClassFile();
  7. // If the value of saveGeneratedFiles is true, the bytecode of the generated proxy class is saved to hard disk
  8. if (saveGeneratedFiles) {
  9. java.security.AccessController.doPrivileged(
  10. new java.security.PrivilegedAction<Void>() {
  11. public Void run() {
  12. try {
  13. FileOutputStream file =
  14. new FileOutputStream(dotToSlash(name) + “.class”);
  15. file.write(classFile);
  16. file.close();
  17. return null;
  18. } catch (IOException e) {
  19. throw new InternalError(
  20. “I/O exception saving generated file: ” + e);
  21. }
  22. }
  23. });
  24. }
  25. // Returns the bytecode of the proxy class
  26. return classFile;
  27. }

Now it’s clear how the JDK dynamically generates bytes of a proxy class. Now, there is another problem, which is who calls the Invoke method of InvocationHandler. The way to solve this problem is to look at what the JDK actually generates for us. Use the following code to get the bytecode generated by the JDK for us and write it to the hard disk.

Java code

  1. package dynamic.proxy;
  2. import java.io.FileOutputStream;
  3. import java.io.IOException;
  4. import sun.misc.ProxyGenerator;
  5. / * *
  6. * Proxy class generation tools
  7. * @author zyb
  8. * @since 2012-8-9
  9. * /
  10. public class ProxyGeneratorUtils {
  11. / * *
  12. * Writes the bytecode of the proxy class to hard disk
  13. * @param path Save path
  14. * /
  15. public static void writeProxyClassToHardDisk(String path) {
  16. // The first method is already known from the ProxyGenerator analysis
  17. // System.getProperties().put(“sun.misc.ProxyGenerator.saveGeneratedFiles”, true);
  18. // The second method
  19. // Get the bytecode of the proxy class
  20. byte[] classFile = ProxyGenerator.generateProxyClass(“$Proxy11”, UserServiceImpl.class.getInterfaces());
  21. FileOutputStream out = null;
  22. try {
  23. out = new FileOutputStream(path);
  24. out.write(classFile);
  25. out.flush();
  26. } catch (Exception e) {
  27. e.printStackTrace();
  28. } finally {
  29. try {
  30. out.close();
  31. } catch (IOException e) {
  32. e.printStackTrace();
  33. }
  34. }
  35. }
  36. }
  37. package dynamic.proxy;
  38. import org.junit.Test;
  39. / * *
  40. * Dynamic proxy test classes
  41. * @author zyb
  42. * @since 2012-8-9
  43. *
  44. * /
  45. public class ProxyTest {
  46. @Test
  47. public void testProxy() throws Throwable {
  48. // Instantiate the target object
  49. UserService userService = new UserServiceImpl();
  50. // Instantiate InvocationHandler
  51. MyInvocationHandler invocationHandler = new MyInvocationHandler(userService);
  52. // Generate a proxy object based on the target object
  53. UserService proxy = (UserService) invocationHandler.getProxy();
  54. // Call the method of the proxy object
  55. proxy.add();
  56. }
  57. @Test
  58. public void testGenerateProxyClass() {
  59. ProxyGeneratorUtils.writeProxyClassToHardDisk(“F:/$Proxy11.class”);
  60. }
  61. }

$proxy. class = $proxy. class = $proxy. class = $proxy. class = $proxy. class = $proxy. class = $proxy. class = $proxy. class

Java code

  1. // Decomcompiled by DJ V3.11.11.95 Copyright 2009 Atanas Neshkov Date: 2012/8/9 20:11:32
  2. // Home Page: http://members.fortunecity.com/neshkov/dj.html http://www.neshkov.com/dj.html – Check often for new version!
  3. // Decompiler options: packimports(3)
  4. import dynamic.proxy.UserService;
  5. import java.lang.reflect.*;
  6. public final class $Proxy11 extends Proxy
  7. implements UserService
  8. {
  9. // the constructor argument is an instance of the MyInvocationHandler class just passed in
  10. public $Proxy11(InvocationHandler invocationhandler)
  11. {
  12. super(invocationhandler);
  13. }
  14. public final boolean equals(Object obj)
  15. {
  16. try
  17. {
  18. return ((Boolean)super.h.invoke(this, m1, new Object[] {
  19. obj
  20. })).booleanValue();
  21. }
  22. catch(Error _ex) { }
  23. catch(Throwable throwable)
  24. {
  25. throw new UndeclaredThrowableException(throwable);
  26. }
  27. }
  28. / * *
  29. * This method is the key part
  30. * /
  31. public final void add()
  32. {
  33. try
  34. {
  35. MyInvocationHandler public Object Invoke (Object Proxy, Method Method, Object[] args)
  36. super.h.invoke(this, m3, null);
  37. return;
  38. }
  39. catch(Error _ex) { }
  40. catch(Throwable throwable)
  41. {
  42. throw new UndeclaredThrowableException(throwable);
  43. }
  44. }
  45. public final int hashCode()
  46. {
  47. try
  48. {
  49. return ((Integer)super.h.invoke(this, m0, null)).intValue();
  50. }
  51. catch(Error _ex) { }
  52. catch(Throwable throwable)
  53. {
  54. throw new UndeclaredThrowableException(throwable);
  55. }
  56. }
  57. public final String toString()
  58. {
  59. try
  60. {
  61. return (String)super.h.invoke(this, m2, null);
  62. }
  63. catch(Error _ex) { }
  64. catch(Throwable throwable)
  65. {
  66. throw new UndeclaredThrowableException(throwable);
  67. }
  68. }
  69. private static Method m1;
  70. private static Method m3;
  71. private static Method m0;
  72. private static Method m2;
  73. // Get four methods in static code block: Equals (Object), Add (UserService), hashCode (Object), toString (Object)
  74. static
  75. {
  76. try
  77. {
  78. m1 = Class.forName(“java.lang.Object”).getMethod(“equals”, new Class[] {
  79. Class.forName(“java.lang.Object”)
  80. });
  81. m3 = Class.forName(“dynamic.proxy.UserService”).getMethod(“add”, new Class[0]);
  82. m0 = Class.forName(“java.lang.Object”).getMethod(“hashCode”, new Class[0]);
  83. m2 = Class.forName(“java.lang.Object”).getMethod(“toString”, new Class[0]);
  84. }
  85. catch(NoSuchMethodException nosuchmethodexception)
  86. {
  87. throw new NoSuchMethodError(nosuchmethodexception.getMessage());
  88. }
  89. catch(ClassNotFoundException classnotfoundexception)
  90. {
  91. throw new NoClassDefFoundError(classnotfoundexception.getMessage());
  92. }
  93. }
  94. }




So far, the first two problems have been known, now use the JDK dynamic proxy will not only use, really achieve the purpose of “know what is, know why”…