The last chapter is the JDK dynamic Proxy implementation principle, this is a self-implementation of dynamic Proxy case, this chapter we custom Proxy, Proxy business needs to achieve the Handler interface, as well as the ClassLoader ClassLoader; Finally, we write our own code to generate the code of the agent class, and then use the code of the agent class to execute our business code, to complete a set of standard dynamic proxy;
This blog is a direct link to Java dynamic proxy.
- JDK dynamic proxy analysis
- Analysis of Cglib dynamic proxy
- JDK dynamic proxy
- JDK dynamic proxy in depth analysis and writing simple JDK dynamic proxy
The first part is the analysis of the JDK dynamic Proxy implementation principle, the next part is a self-implementation of dynamic Proxy case, this part we custom Proxy, Proxy business needs to achieve the Handler interface, and the ClassLoader ClassLoader; Finally, we write our own code to generate the code of the agent class, and then use the code of the agent class to execute our business code, and complete a standard dynamic agent process;
First, let’s look at what it takes to implement a Proxy. Here’s the newProxyInstance() method that Proxy generates for the Proxy class:
Proxy.newProxyInstance(ClassLoader loader, Class<? >[] interfaces, InvocationHandler h)Copy the code
A Proxy class, a ClassLoader, an array of interfaces implemented by business classes, and an InvocationHandler; Breaking down the steps here is the following two steps:
1.proxy is actually based on the Class<? >[] interfaces to generate proxy class $Proxy0;2$Proxy0; $Proxy0; $Proxy0;Copy the code
Now we do it step by step. In Proxy, we can roughly divide it into four steps:
1Dynamic generation of proxy class source code. Java file, and write to disk;2. Compile the generated. Java file into a. Class file.3. Load the compiled. Class file into the JVM;4. Returns a dynamically generated proxy object;Copy the code
The GuituDynamicProxy class will look like this:
package com.guitu18.study.proxy.guitu;
import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
/** * self-implementing dynamic proxy **@author zhangkuan
* @email [email protected]
* @DateBetter 2019/1/1 * /
public class GuituDynamicProxy {
/** * newline */
private static final String LN = "\r\n";
/** * specifies the name of the generated proxy class
private static final String SRC_NAME = "$GuituProxy0";
/** * The package name of the generated proxy class, again defined directly as a string */ for testing purposes
private static final String PACKAGE_NAME = "com.guitu18.study.proxy.guitu";
/** * generates and returns a proxy object **@paramGuituClassLoader self-implemented class loader *@paramInterfaces All interfaces implemented by a proxy class@paramA {guituInvocationHandler@linkImplementation of the GuituInvocationHandler} interface * The code that our proxy class enhances its proxy objects is written in the implementation of this interface * {@link GuituProxy#invoke(Object, Method, Object[])}
* @returnReturns the generated proxy object */
public static Object newProxyInstance(GuituClassLoader guituClassLoader, Class
[] interfaces, GuituInvocationHandler guituInvocationHandler) {
try {
// 1. Dynamically generate source code. Java file and write to disk
File file = generateSrcToFile(interfaces);
// 2. Compile the generated. Java file into a. Class file
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager manage = compiler.getStandardFileManager(null.null.null);
Iterable iterable = manage.getJavaFileObjects(file);
JavaCompiler.CompilationTask task =
compiler.getTask(null, manage, null.null.null, iterable);
task.call();
manage.close();
// 3. Load the compiled. Class file into the JVM
Class proxyClass = guituClassLoader.findClass(SRC_NAME);
Constructor constructor = proxyClass.getConstructor(GuituInvocationHandler.class);
// 4. Return the dynamically generated proxy object
return constructor.newInstance(guituInvocationHandler);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/** * Code generation is simple and efficient for understanding and learning@paramInterfaces All interfaces implemented by a proxy class@returnReturns the generated source code File object */
private static File generateSrcToFile(Class
[] interfaces) {
try {
StringBuffer sb = new StringBuffer();
sb.append("package " + PACKAGE_NAME + ";" + LN);
sb.append("import java.lang.reflect.Method;" + LN);
/** * implements all interfaces */
StringBuffer interfaceStr = new StringBuffer();
for (int i = 0; i < interfaces.length; i++) {
interfaceStr.append(interfaces[i].getName());
if (interfaces.length > 1 && i < interfaces.length - 2) {
interfaceStr.append(",");
}
}
sb.append("public class " + SRC_NAME + " implements " + interfaceStr.toString() + "{" + LN);
sb.append(" GuituInvocationHandler guituInvocationHandler;" + LN);
sb.append(" public " + SRC_NAME + "(GuituInvocationHandler guituInvocationHandler) { " + LN);
sb.append(" this.guituInvocationHandler = guituInvocationHandler;" + LN);
sb.append("}" + LN);
/** * all methods that implement all interfaces */
for(Class<? > anInterface : interfaces) {for (Method method : anInterface.getMethods()) {
// method type parameter group
Parameter[] parameters = method.getParameters();
// method method parameter, type name string
StringBuffer paramStr = new StringBuffer();
// Method parameter type string
StringBuffer paramTypeStr = new StringBuffer();
// Method parameter name string
StringBuffer paramNameStr = new StringBuffer();
for (int i = 0; i < parameters.length; i++) {
Parameter parameter = parameters[i];
// Concatenation method parameter, type name
paramStr.append(parameter.getType().getName() + "" + parameter.getName());
// Concatenate method parameter types for reflection calls
paramTypeStr.append(parameter.getType().getName()).append(".class");
// Concatenate method parameter names for reflection calls
paramNameStr.append(parameter.getName());
if (parameters.length > 1 && i < parameters.length - 2) {
sb.append(",");
paramTypeStr.append(",");
paramNameStr.append(","); }}// Generate method
String returnTypeName = method.getReturnType().getName();
sb.append(" public " + returnTypeName + "" + method.getName() + "(" + paramStr.toString() + "{" + LN);
sb.append(" try{" + LN);
sb.append(" Method method = " + interfaces[0].getName() +
".class.getMethod(\"" + method.getName() + "\",new Class[]{" + paramTypeStr.toString() + "});" + LN);
// Check whether the method returns a value
if (!"void".equals(returnTypeName)) {
sb.append("" + returnTypeName +
" invoke = (" + returnTypeName + ")this.guituInvocationHandler.invoke(this, method, new Object[]{"
+ paramNameStr.toString() + "});" + LN);
sb.append(" return invoke;" + LN);
} else {
sb.append(" this.guituInvocationHandler.invoke(this, method, null);" + LN);
}
sb.append(" }catch(Throwable e){" + LN);
sb.append(" e.printStackTrace();" + LN);
sb.append("}" + LN);
if (!"void".equals(method.getReturnType().getName())) {
sb.append(" return null;" + LN);
}
sb.append("}" + LN);
}
}
sb.append("}" + LN);
// Writes the generated bytecode to a disk file
String path = GuituDynamicProxy.class.getResource("").getPath();
System.out.println(path);
File file = new File(path + SRC_NAME + ".java");
FileWriter fw = new FileWriter(file);
fw.write(sb.toString());
fw.flush();
fw.close();
return file;
} catch (Exception e) {
e.printStackTrace();
}
return null; }}Copy the code
In the previous step, we became the proxy class, and then compiled it into a class file using JavaCompiler. Then we loaded the class file into memory using the ClassLoader. The findClass(String name) method is overridden by the ClassLoader class.
package com.guitu18.study.proxy.guitu;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
/** * self-implemented class loader **@author zhangkuan
* @email [email protected]
* @Date2019/1/1 15:51 * /
public class GuituClassLoader extends ClassLoader {
private File classPathFile;
/** * constructor to create the generated file */
public GuituClassLoader(a) {
this.classPathFile = new File(GuituClassLoader.class.getResource("").getPath());
}
/** * get the bytecode object **@param name
* @return
* @throws ClassNotFoundException
*/
@Override
protectedClass<? > findClass(String name)throws ClassNotFoundException {
String className = GuituClassLoader.class.getPackage().getName() + "." + name;
if(classPathFile ! =null) {
File classFile = new File(classPathFile, name.replaceAll("\ \."."/") + ".class");
if (classFile.exists()) {
FileInputStream in = null;
ByteArrayOutputStream out = null;
try {
in = new FileInputStream(classFile);
out = new ByteArrayOutputStream();
byte[] buff = new byte[1024];
int len;
while((len = in.read(buff)) ! = -1) {
out.write(buff, 0, len);
}
return defineClass(className, out.toByteArray(), 0, out.size());
} catch (Exception e) {
e.printStackTrace();
} finally {
if (null! = in) {try {
in.close();
} catch(IOException e) { e.printStackTrace(); }}if(out ! =null) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
return null; }}Copy the code
Interface GuituInvocationHandler (equivalent to InvocationHandler) :
package com.guitu18.study.proxy.guitu;
import java.lang.reflect.Method;
/** * The proxy class needs to implement this interface, overriding the invoke method **@author zhangkuan
* @email [email protected]
* @Date2019/1/1 15:18 * /
public interface GuituInvocationHandler {
/** * This method is required when the proxy class enhances the business, and the dynamic proxy ultimately calls the implementation of this method **@paramProxy Indicates the generated proxy class *@paramMethod Method of the proxy *@paramMethod parameter * for the ARGS proxy@returnReturns the result of agent execution */
Object invoke(Object proxy, Method method, Object[] args);
}
Copy the code
With these three things, we can use them to write our dynamic proxies in the same way we used JDK dynamic proxies in the previous article, only with our own code:
package com.guitu18.study.proxy.guitu;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/** * Proxy class **@author zhangkuan
* @email [email protected]
* @Date2019/1/1 16:01 * /
public class GuituProxy implements GuituInvocationHandler {
private Object target;
/** * get the proxy object **@paramObject The proxy object *@returnReturn the proxy class */
public Object getInstance(Object object) {
try {
this.target = object;
return GuituDynamicProxy.newProxyInstance(new GuituClassLoader(), object.getClass().getInterfaces(), this);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/** * The business logic before and after the proxy execution, which is called by the generated proxy class **@paramProxy Proxy object *@paramMethod The method executed by the agent *@paramMethod parameter * executed by the ARGS agent@returnReturns the result of the execution of the proxy method. The returned Object Object is strongly transformed by the generated proxy class based on the return value of the proxy method
@Override
public Object invoke(Object proxy, Method method, Object[] args) {
try {
System.out.println("Guitu dynamic proxy, before proxy execution...");
Object invoke = null;
invoke = method.invoke(this.target, args);
System.out.println("After execution...");
return invoke;
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return null; }}Copy the code
At this point a set of JDK dynamic proxy implementation is completed, this process many direct use of simplified operations, JDK dynamic proxy source is more complex than this, this article mainly to strengthen the understanding of JDK dynamic proxy ideas; The specific step analysis and process description I have written in the above code comments are very detailed, here will not do too much explanation; This is a little bit more complicated is the dynamic generation of proxy class source code this step, here need to be very careful, after all, using string concatenation code, no error; Other processes are easy if you understand the principles; Here’s a simple post of the test code:
package com.guitu18.study.proxy.guitu;
import com.guitu18.study.proxy.Persion;
import com.guitu18.study.proxy.ZhangKuan;
/** * self-implementing dynamic proxy test class **@author zhangkuan
* @email [email protected]
* @Date2019/1/1 not * /
public class GuituProxyTest {
public static void main(String[] args) {
Persion instance = (Persion) new GuituProxy().getInstance(new ZhangKuan());
String love = instance.findLove("Pale and beautiful with long legs."); System.out.println(love); instance.findWord(); }}Copy the code
The business interface Persion and business class ZhangKuan in the test are not pasted here, and the code is exactly the same as in the previous article; The result is as follows:
Guitu Dynamic proxy, proxy before execution... White, beautiful, long legs... Ye Qing I love you Guitu dynamic agent, agent before executing... I'm looking for a monthly salary15-25After k's work is executed...Copy the code
This is the end of the JDK dynamic proxy in-depth analysis, Java learning has a long way to go, 2019 continue to work hard, keep up the good work!