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.
- package dynamic.proxy;
- import java.lang.reflect.InvocationHandler;
- import java.lang.reflect.Method;
- import java.lang.reflect.Proxy;
- / * *
- * Implement your own InvocationHandler
- * @author zyb
- * @since 2012-8-9
- *
- * /
- public class MyInvocationHandler implements InvocationHandler {
- // Target object
- private Object target;
- / * *
- * Construction method
- * @param target Target object
- * /
- public MyInvocationHandler(Object target) {
- super();
- this.target = target;
- }
- / * *
- * The method that executes the target object
- * /
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- // Simply print the target object’s method before executing it
- System.out.println(“——————before——————“);
- // Execute the target object’s method
- Object result = method.invoke(target, args);
- // Simply print after the target object’s method is executed
- System.out.println(“——————-after——————“);
- return result;
- }
- / * *
- * Gets the proxy object of the target object
- * @return Proxy object
- * /
- public Object getProxy() {
- return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
- target.getClass().getInterfaces(), this);
- }
- }
- package dynamic.proxy;
- / * *
- * Target object implementation interface, using JDK to generate proxy objects must implement an interface
- * @author zyb
- * @since 2012-8-9
- *
- * /
- public interface UserService {
- / * *
- * Target method
- * /
- public abstract void add();
- }
- package dynamic.proxy;
- / * *
- * Target object
- * @author zyb
- * @since 2012-8-9
- *
- * /
- public class UserServiceImpl implements UserService {
- /* (non-Javadoc)
- * @see dynamic.proxy.UserService#add()
- * /
- public void add() {
- System.out.println(“——————–add—————“);
- }
- }
- package dynamic.proxy;
- import org.junit.Test;
- / * *
- * Dynamic proxy test classes
- * @author zyb
- * @since 2012-8-9
- *
- * /
- public class ProxyTest {
- @Test
- public void testProxy() throws Throwable {
- // Instantiate the target object
- UserService userService = new UserServiceImpl();
- // Instantiate InvocationHandler
- MyInvocationHandler invocationHandler = new MyInvocationHandler(userService);
- // Generate a proxy object based on the target object
- UserService proxy = (UserService) invocationHandler.getProxy();
- // Call the method of the proxy object
- proxy.add();
- }
- }
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.
- / * *
- * loader: class loader
- * interfaces: Interfaces implemented by the target object
- * h: Implementation class of InvocationHandler
- * /
- public static Object newProxyInstance(ClassLoader loader,
- Class
[] interfaces, - InvocationHandler h)
- throws IllegalArgumentException
- {
- if (h == null) {
- throw new NullPointerException();
- }
- / *
- * Look up or generate the designated proxy class.
- * /
- Class cl = getProxyClass(loader, interfaces);
- / *
- * Invoke its constructor with the designated invocation handler.
- * /
- try {
- // Call the constructor of the proxy object (i.e. $Proxy0(InvocationHandler h))
- Constructor cons = cl.getConstructor(constructorParams);
- // Generate an instance of the proxy class and pass an instance of MyInvocationHandler to its constructor
- return (Object) cons.newInstance(new Object[] { h });
- } catch (NoSuchMethodException e) {
- throw new InternalError(e.toString());
- } catch (IllegalAccessException e) {
- throw new InternalError(e.toString());
- } catch (InstantiationException e) {
- throw new InternalError(e.toString());
- } catch (InvocationTargetException e) {
- throw new InternalError(e.toString());
- }
- }
Let’s go back to the getProxyClass method
- public static Class
getProxyClass(ClassLoader loader, - Class
… interfaces) - throws IllegalArgumentException
- {
- // Throw an exception if the target class implements more than 65535 interfaces.
- if (interfaces.length > 65535) {
- throw new IllegalArgumentException(“interface limit exceeded”);
- }
- // Declare the Class object represented by the proxy object.
- Class proxyClass = null;
- String[] interfaceNames = new String[interfaces.length];
- Set interfaceSet = new HashSet(); // for detecting duplicates
- // Iterate over the interface implemented by the target class
- for (int i = 0; i < interfaces.length; i++) {
- // Get the name of the interface implemented by the target class
- String interfaceName = interfaces[i].getName();
- Class interfaceClass = null;
- try {
- // Load the interface implemented by the target class into memory
- interfaceClass = Class.forName(interfaceName, false, loader);
- } catch (ClassNotFoundException e) {
- }
- if (interfaceClass ! = interfaces[i]) {
- throw new IllegalArgumentException(
- interfaces[i] + ” is not visible from class loader”);
- }
- // Omit some irrelevant code…….
- // Put the Class object represented by the interface implemented by the target Class into the Set
- interfaceSet.add(interfaceClass);
- interfaceNames[i] = interfaceName;
- }
- // Use the name of the interface implemented by the target class as the key in the cache (Map)
- Object key = Arrays.asList(interfaceNames);
- Map cache;
- synchronized (loaderToCache) {
- // Fetch the cache from the cache
- cache = (Map) loaderToCache.get(loader);
- if (cache == null) {
- // If not, create another HashMap instance
- cache = new HashMap();
- // Put the HashMap instance and the current loader in the cache
- loaderToCache.put(loader, cache);
- }
- }
- synchronized (cache) {
- do {
- // Retrieve objects from the cache based on the name of the interface
- Object value = cache.get(key);
- if (value instanceof Reference) {
- proxyClass = (Class) ((Reference) value).get();
- }
- if (proxyClass ! = null) {
- // If the Class instance of the proxy object already exists, it is returned directly
- return proxyClass;
- } else if (value == pendingGenerationMarker) {
- try {
- cache.wait();
- } catch (InterruptedException e) {
- }
- continue;
- } else {
- cache.put(key, pendingGenerationMarker);
- break;
- }
- } while (true);
- }
- try {
- // omit some code…….
- // This is the key to dynamically generating proxy objects
- byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
- proxyName, interfaces);
- try {
- // Generate an instance of the proxy class based on its bytecode
- proxyClass = defineClass0(loader, proxyName,
- proxyClassFile, 0, proxyClassFile.length);
- } catch (ClassFormatError e) {
- throw new IllegalArgumentException(e.toString());
- }
- }
- // add to set of all generated proxy classes, for isProxyClass
- proxyClasses.put(proxyClass, null);
- }
- // omit some code…….
- return proxyClass;
- }
Go to the static method generateProxyClass of the ProxyGenerator class and this is where the bytecode of the proxy class is actually generated.
- public static byte[] generateProxyClass(final String name,
- Class[] interfaces)
- {
- ProxyGenerator gen = new ProxyGenerator(name, interfaces);
- // Generate the bytecode of the proxy class dynamically
- final byte[] classFile = gen.generateClassFile();
- // If the value of saveGeneratedFiles is true, the bytecode of the generated proxy class is saved to hard disk
- if (saveGeneratedFiles) {
- java.security.AccessController.doPrivileged(
- new java.security.PrivilegedAction<Void>() {
- public Void run() {
- try {
- FileOutputStream file =
- new FileOutputStream(dotToSlash(name) + “.class”);
- file.write(classFile);
- file.close();
- return null;
- } catch (IOException e) {
- throw new InternalError(
- “I/O exception saving generated file: ” + e);
- }
- }
- });
- }
- // Returns the bytecode of the proxy class
- return classFile;
- }
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.
- package dynamic.proxy;
- import java.io.FileOutputStream;
- import java.io.IOException;
- import sun.misc.ProxyGenerator;
- / * *
- * Proxy class generation tools
- * @author zyb
- * @since 2012-8-9
- * /
- public class ProxyGeneratorUtils {
- / * *
- * Writes the bytecode of the proxy class to hard disk
- * @param path Save path
- * /
- public static void writeProxyClassToHardDisk(String path) {
- // The first method is already known from the ProxyGenerator analysis
- // System.getProperties().put(“sun.misc.ProxyGenerator.saveGeneratedFiles”, true);
- // The second method
- // Get the bytecode of the proxy class
- byte[] classFile = ProxyGenerator.generateProxyClass(“$Proxy11”, UserServiceImpl.class.getInterfaces());
- 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();
- }
- }
- }
- }
- package dynamic.proxy;
- import org.junit.Test;
- / * *
- * Dynamic proxy test classes
- * @author zyb
- * @since 2012-8-9
- *
- * /
- public class ProxyTest {
- @Test
- public void testProxy() throws Throwable {
- // Instantiate the target object
- UserService userService = new UserServiceImpl();
- // Instantiate InvocationHandler
- MyInvocationHandler invocationHandler = new MyInvocationHandler(userService);
- // Generate a proxy object based on the target object
- UserService proxy = (UserService) invocationHandler.getProxy();
- // Call the method of the proxy object
- proxy.add();
- }
- @Test
- public void testGenerateProxyClass() {
- ProxyGeneratorUtils.writeProxyClassToHardDisk(“F:/$Proxy11.class”);
- }
- }
$proxy. class = $proxy. class = $proxy. class = $proxy. class = $proxy. class = $proxy. class = $proxy. class = $proxy. class
- // Decomcompiled by DJ V3.11.11.95 Copyright 2009 Atanas Neshkov Date: 2012/8/9 20:11:32
- // Home Page: http://members.fortunecity.com/neshkov/dj.html http://www.neshkov.com/dj.html – Check often for new version!
- // Decompiler options: packimports(3)
- import dynamic.proxy.UserService;
- import java.lang.reflect.*;
- public final class $Proxy11 extends Proxy
- implements UserService
- {
- // the constructor argument is an instance of the MyInvocationHandler class just passed in
- public $Proxy11(InvocationHandler invocationhandler)
- {
- super(invocationhandler);
- }
- public final boolean equals(Object obj)
- {
- try
- {
- return ((Boolean)super.h.invoke(this, m1, new Object[] {
- obj
- })).booleanValue();
- }
- catch(Error _ex) { }
- catch(Throwable throwable)
- {
- throw new UndeclaredThrowableException(throwable);
- }
- }
- / * *
- * This method is the key part
- * /
- public final void add()
- {
- try
- {
- MyInvocationHandler public Object Invoke (Object Proxy, Method Method, Object[] args)
- super.h.invoke(this, m3, null);
- return;
- }
- catch(Error _ex) { }
- catch(Throwable throwable)
- {
- throw new UndeclaredThrowableException(throwable);
- }
- }
- public final int hashCode()
- {
- try
- {
- return ((Integer)super.h.invoke(this, m0, null)).intValue();
- }
- catch(Error _ex) { }
- catch(Throwable throwable)
- {
- throw new UndeclaredThrowableException(throwable);
- }
- }
- public final String toString()
- {
- try
- {
- return (String)super.h.invoke(this, m2, null);
- }
- catch(Error _ex) { }
- catch(Throwable throwable)
- {
- throw new UndeclaredThrowableException(throwable);
- }
- }
- private static Method m1;
- private static Method m3;
- private static Method m0;
- private static Method m2;
- // Get four methods in static code block: Equals (Object), Add (UserService), hashCode (Object), toString (Object)
- static
- {
- try
- {
- m1 = Class.forName(“java.lang.Object”).getMethod(“equals”, new Class[] {
- Class.forName(“java.lang.Object”)
- });
- m3 = Class.forName(“dynamic.proxy.UserService”).getMethod(“add”, new Class[0]);
- m0 = Class.forName(“java.lang.Object”).getMethod(“hashCode”, new Class[0]);
- m2 = Class.forName(“java.lang.Object”).getMethod(“toString”, new Class[0]);
- }
- catch(NoSuchMethodException nosuchmethodexception)
- {
- throw new NoSuchMethodError(nosuchmethodexception.getMessage());
- }
- catch(ClassNotFoundException classnotfoundexception)
- {
- throw new NoClassDefFoundError(classnotfoundexception.getMessage());
- }
- }
- }
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”…