如果你要自己實作一個工具類,實作動态建立代理類,怎麼實作?
1、動态生成一個類:通過字元串拼接生成一個類:$Proxy0 ,這個類就是代理類
2、将代理類的字元串形式用流的方式寫到磁盤$Proxy0.java檔案
3、将$Proxy0.java類編譯成$Proxy0.class檔案,通過程式動态編譯
4、自定義類加載器,加載編譯好的 $Proxy0.class 到JVM記憶體
5、通過加載到記憶體的$Proxy0類位元組碼 建立一個代理類的執行個體并傳回執行個體
以下示例代碼自測通過,可以拷貝調試檢視效果最佳
被代理類實作的接口 Person
public interface Person {
public void makeFriends() throws Throwable;
}
被代理類的增強邏輯,通過EternalMother承載
public class EternalMother implements EternalInvocationHandler {
public Son mySon;
public EternalMother(Son mySon) {
this.mySon = mySon;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
blindDate();
mySon.makeFriends();
appointment();
return null;
}
private void blindDate() {
System.out.println("EternalMother------------通過相親認識很多妹子");
}
private void appointment() {
System.out.println("EternalMother------------天天和女朋友約會");
}
}
EternalInvocationHandler 模拟 InvocationHandler 定義一個調用接口,後面會用到
package com.eternal.myproxy;
import java.lang.reflect.Method;
public interface EternalInvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
EternalClassLoader 自定義的類加載器,加載動态建立的代理類
package com.eternal.myproxy;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
public class EternalClassLoader extends ClassLoader {
private File dir;
public EternalClassLoader(String path) {
this.dir = new File(path);
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
if (dir != null) {
File clazzFile = new File(dir, name + ".class");
if (clazzFile.exists()) {
try {
FileInputStream fileInputStream = new FileInputStream(clazzFile);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len;
while ((len = fileInputStream.read(buffer)) != -1) {
byteArrayOutputStream.write(buffer, 0, len);
return defineClass(
"com.eternal.myproxy." + name
, byteArrayOutputStream.toByteArray()
, 0
, byteArrayOutputStream.size());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
return super.findClass(name);
}
}
EternalProxy 建立代理對象的工具類,jdk實作了一個Proxy,我們現在自己實作一個EternalProxy
package com.eternal.myproxy;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
public class EternalProxy {
private static String path = EternalProxy.class.getResource("").getPath().replace("build/classes/java/main", "src/main/java");
private static String className = "$Proxy0";
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
EternalInvocationHandler h) {
//1、動态生成一個類:通過字元串拼接生成一個類:$Proxy0
String javaClassStr = createJavaClassStr(interfaces);
File file = new File(path + "\\" + className + ".java");
//2、将字元串用流的方式寫到磁盤$Proxy0.java檔案
createJavaClassFile(javaClassStr);
//3、将$Proxy0.java類編譯成$Proxy0.class檔案
compile();
//4、自定義類加載器,加載$Proxy0.class 到JVM記憶體
EternalClassLoader eternalClassLoader = new EternalClassLoader(path);
//5、建立一個代理類的執行個體并傳回執行個體
try {
Class<?> clazzOf$Proxy0 = eternalClassLoader.findClass(className);
Constructor<?> constructor = clazzOf$Proxy0.getConstructor(EternalInvocationHandler.class);
Object proxyInstance = constructor.newInstance(h);
return proxyInstance;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private static void compile() {
try {
JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager javaFileManager = javaCompiler.getStandardFileManager(null, null, null);
Iterable<? extends JavaFileObject> javaFileObjects = javaFileManager.getJavaFileObjects(path + "\\" + className + ".java");
JavaCompiler.CompilationTask task = javaCompiler.getTask(null,
javaFileManager,
null,
null,
null,
javaFileObjects);
task.call();
javaFileManager.close();
} catch (Exception e) {
e.printStackTrace();
}
}
private static void createJavaClassFile(String javaClassStr) {
File file = new File(path + "\\" + className + ".java");
FileWriter fileWriter = null;
try {
fileWriter = new FileWriter(file);
fileWriter.write(javaClassStr);
fileWriter.flush();
fileWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
private static String createJavaClassStr(Class<?>[] interfaces) {
Method[] methods = interfaces[0].getMethods();
StringBuilder javaClassStrSB = new StringBuilder();
javaClassStrSB.append("package com.eternal.myproxy;").append(System.lineSeparator());
javaClassStrSB.append("import java.lang.reflect.Method;").append(System.lineSeparator());
javaClassStrSB.append("public class ").append(className).append(" implements ").append(interfaces[0].getName()).append("{")
.append(System.lineSeparator());
javaClassStrSB.append(" public EternalInvocationHandler h;").append(System.lineSeparator());
javaClassStrSB.append("public ").append(className).append("(").append("EternalInvocationHandler h){").append(System.lineSeparator());
javaClassStrSB.append("this.h=h;").append(System.lineSeparator()).append("}").append(System.lineSeparator());
javaClassStrSB.append(createMethodStr(methods, interfaces[0]));
return javaClassStrSB.toString();
}
private static String createMethodStr(Method[] methods, Class interfaceOne) {
StringBuilder javaMethodsStrSB = new StringBuilder();
for (Method method : methods) {
javaMethodsStrSB
.append("public void ").append(method.getName()).append("() throws Throwable {").append(System.lineSeparator())
.append("Method md = ").append(interfaceOne.getName()).append(".class.getMethod(\"").append(method.getName())
.append("\",new Class[]{});").append(System.lineSeparator())
.append("this.h.invoke(this,md,null);").append(System.lineSeparator()).append("}").append(System.lineSeparator());
}
javaMethodsStrSB.append("}");
return javaMethodsStrSB.toString();
}
}
測試類:EternalProxyTest
- 被代理類 Son
- 被代理類的增強邏輯類 EternalMother類
- 代理類,通過自定義的EternalProxy工具類動态建立,類名 $Proxy0
package com.eternal.myproxy;
import com.eternal.proxy.Person;
import com.eternal.proxy.Son;
import com.eternal.proxy.Test;
public class EternalProxyTest {
public static void main(String[] args) throws Throwable {
EternalMother mother = new EternalMother(new Son());
Person proxyObject = (Person) EternalProxy.newProxyInstance(
EternalProxyTest.class.getClassLoader()
, new Class<?>[]{Person.class}
, mother);
proxyObject.makeFriends();
}
}
測試類執行結果:
JDK動态代理-原理分析 通過 java.lang.reflect.Proxy 類實作動态建立代理類
本文通過手寫一個 EternalProxy類,實作 Proxy同樣的邏輯,以此來解密動态建立代理類并傳回代理執行個體的底層密碼