在学习代理模式的过程中,特别是调用javaAPI的动态代理Proxy这个类的时候,发现程序会自动生成代理类,于是乎对于他是如何生成,以及生成之后存放在哪里产生了好奇.
由于查看代码的难度较高,我采用了猜想验证的方式去读代码,很多地方只能通过推测去猜
//通过设置这个参数,可以将程序动态生成的代理保存下来
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
Objects.requireNonNull(h);
final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
/*
* 用来加载代理类
*/
Class<?> cl = getProxyClass0(loader, intfs);
/*
* 对已经加载进来的代理类进行调用.后续补充...
*/
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}
猜想
对于生成的.class文件的猜想
猜想1:通过sun.misc.ProxyGenerator.saveGeneratedFiles这个参数,可以将中间生成的代理类进行保存到本地,所以在配置为flase的时候,他应该是将产生的代理类文件缓存到内存了
猜想2:我们知道程序要加载代理类实现我们需要的功能,少不了一个关键的东西,那就是类加载器,而在学习过程中,我们知道类加载的过程分有->加载,验证,准备,解析和初始化这五个阶段,所以我们猜想通过Proxy类的newProxyInstance()这个方法的参数可以得知,传入了一个类加载器对象,保证了生成的.class文件可以被我们指定的类加载器加载,传入的interfaces,一方面可以提供在构建代理类文件的时候,里面的接口实现以及方法继承,另一方面,可以直接通过反射进行方法调用…
验证
Object o = Proxy.newProxyInstance(Url_req.class.getClassLoader(), new Class[]{FilterInter.class},
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("methon " + method.getName() + "start");
Object invoke2 = method.invoke(url_req,args);
System.out.println("methon " + method.getName() + "end");
return invoke2;
}
});
基于上次写的代码,查看代理对象在生成代理类对象之前发生了什么
进入newProxyInstance这个方法中可以看到上面在上面那段代码,中最重要的就是Class<?> cl = getProxyClass0(loader, intfs);
从字面意义上看,这个方法明显就是用来获取代理类的,那么代理类的生成和加载也肯定发生在这个方法中!
因我们知道想用反射去生成对象,或者调用类里面的方法的前提是,你以及将对应的类加载进来了,你才能进行反射,(比如Class.forName等等这几种反射方法.用的是默认的类加载器,记得好像是application class loader)将类加载进来.
/**
* Generate a proxy class. Must call the checkProxyAccess method
* to perform permission checks before calling this.
*/
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
// If the proxy class defined by the given loader implementing
// the given interfaces exists, this will simply return the cached copy;
// otherwise, it will create the proxy class via the ProxyClassFactory
return proxyClassCache.get(loader, interfaces);
}
方法里面调用了proxyClassCache.get方法,从字面意义可以看出是以一种缓存的形式加载的,
通过这个方法去获得代理类对象
所以我们将重点主要放在proxyClassCache.get(loader, interfaces);这个方法上
这个方法传入了两个参数,一个是loader 加载器,一个intfs 一个Class元素的数组,
看到了我们的目标,proxyClassCache字眼,先前我们便是通过Cache来推测他是将类对象缓存起来了
通过源码的注释也确实可以了解到,里面存放的是一个缓存的proxyClass对象
先贴上完整的get方法的代码
public V get(K key, P parameter) {
Objects.requireNonNull(parameter);
expungeStaleEntries();
Object cacheKey = CacheKey.valueOf(key, refQueue);
// lazily install the 2nd level valuesMap for the particular cacheKey
ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
if (valuesMap == null) {
ConcurrentMap<Object, Supplier<V>> oldValuesMap
= map.putIfAbsent(cacheKey,
valuesMap = new ConcurrentHashMap<>());
if (oldValuesMap != null) {
valuesMap = oldValuesMap;
}
}
// create subKey and retrieve the possible Supplier<V> stored by that
// subKey from valuesMap
Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
Supplier<V> supplier = valuesMap.get(subKey);
Factory factory = null;
while (true) {
if (supplier != null) {
// supplier might be a Factory or a CacheValue<V> instance
V value = supplier.get();
if (value != null) {
return value;
}
}
// else no supplier in cache
// or a supplier that returned null (could be a cleared CacheValue
// or a Factory that wasn't successful in installing the CacheValue)
// lazily construct a Factory
if (factory == null) {
factory = new Factory(key, parameter, subKey, valuesMap);
}
if (supplier == null) {
supplier = valuesMap.putIfAbsent(subKey, factory);
if (supplier == null) {
// successfully installed Factory
supplier = factory;
}
// else retry with winning supplier
} else {
if (valuesMap.replace(subKey, supplier, factory)) {
// successfully replaced
// cleared CacheEntry / unsuccessful Factory
// with our Factory
supplier = factory;
} else {
// retry with current supplier
supplier = valuesMap.get(subKey);
}
}
}
}
通过调试的模式,发现里面有部分参数在第一次调用的时候为null,以及一部分结构是为了并发调用的时候进行的判断,这部分就不详写,能力有限
Objects.requireNonNull(parameter);
expungeStaleEntries();
Object cacheKey = CacheKey.valueOf(key, refQueue);
先进行了检查,传入参数是否为空,然后在调用清楚过期缓存的方法,进行清理
补充:在类对象的创建的时候是分有不同的引用,不同的引用级别会影响GC时候的范围:待补充,因为这里采用的是weakCache:所以对应的也是weakreference 弱引用,很容易成为被GC的对象
所以在CacheKey.valueOf这个方法,我的理解是对这个缓存对象打上一个弱引用的标签(引用–>待补充
)
由于刚开始学的时候,多个参数不明所以,不晓得是什么意思,便采用倒序读
通过走读代码,可以知道
// 在用调试模式进行调试的时候发现,当你第一次调用的时候,最后会通过这个Factory工厂类来进行构建
if (factory == null) {
factory = new Factory(key, parameter, subKey, valuesMap);
}
主要是通过new Factory构建一个工厂,来获取,构建工厂对象所需要的4个参数,
key->loader,
parameter->infers
subKey
valuesMap
通过代码调试可以知道,当程序执行到这里的时候valueMap是为空,subKey则是一个对象,所以我们便定位一下这个subKey
// create subKey and retrieve the possible Supplier<V> stored by that
// subKey from valuesMap
Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
Supplier<V> supplier = valuesMap.get(subKey);
Factory factory = null;
在前面,就有通过subKeyFactory这样的一个工厂类,生成了一个subKey对象
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
//这个位置对传进来的接口数组进行了遍历
for (Class<?> intf : interfaces) {
Class<?> interfaceClass = null;
try {
//通过forName调用了默认的类加载器加载了接口,并且不指定初始化
interfaceClass = Class.forName(intf.getName(), false, loader);
} catch (ClassNotFoundException e) {
}
if (interfaceClass != intf) {
throw new IllegalArgumentException(
intf + " is not visible from class loader");
}
if (!interfaceClass.isInterface()) {
throw new IllegalArgumentException(
interfaceClass.getName() + " is not an interface");
}
if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
throw new IllegalArgumentException(
"repeated interface: " + interfaceClass.getName());
}
}
String proxyPkg = null; // package to define proxy class in
int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
//对代理类的一些基础属性的获取,访问权限,方法等等
for (Class<?> intf : interfaces) {
int flags = intf.getModifiers();
if (!Modifier.isPublic(flags)) {
accessFlags = Modifier.FINAL;
String name = intf.getName();
int n = name.lastIndexOf('.');
String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
if (proxyPkg == null) {
proxyPkg = pkg;
} else if (!pkg.equals(proxyPkg)) {
throw new IllegalArgumentException(
"non-public interfaces from different packages");
}
}
}
if (proxyPkg == null) {
// if no non-public proxy interfaces, use com.sun.proxy package
proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
}
long num = nextUniqueNumber.getAndIncrement();
//构建class名
String proxyName = proxyPkg + proxyClassNamePrefix + num;
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
throw new IllegalArgumentException(e.toString());
}
}
}
通过代码调试,可以比较轻易的定位到Proxy类的apply这个方法下,可以看出,返回的是一个Class 类型的对象,所以在这里应该进行了类加载.
interfaceClass = Class.forName(intf.getName(), false, loader);
在代码577行的位置,调用了Class.forName的方法,进行类的加载,闯入的intf.getName()—>是传入接口全路径,以及false(待会解释),以及一个类加载器,这样一个类加载的条件差不多就齐了
类在加载的时候有五个阶段–>加载,验证,准备,解析和初始化
此处猜想为虽然对类进行加载,但是却不进行初始化,也就是不进行对象的引用,保证类的空引用状态
打入断点调试之后,也可以看到,里面除了类路径名以及加载器,其余的引用都为空
PS:我的理解是,先准备一个符合要求的对象框架,后续在往里面添加一些构建代理类所需要的东西
后续通过interface类进行getname等方法构建类名,package名,访问权限等
通过这个方法,开始构建代理类的字节码(重要)
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
通过传进来的参数,生成一个ProxyGenerator的一个对象,然后在通过这个对象,进行构造一些方法
通过接收类型可以看到是一个字节数组,我的猜想便是程序自行生成的.class文件被翻译成字节码,存到数组中去,或者方便以字节流的形式进行类的加载
public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) {
ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
final byte[] var4 = var3.generateClassFile();
if (saveGeneratedFiles) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
try {
int var1 = var0.lastIndexOf(46);
Path var2;
if (var1 > 0) {
Path var3 = Paths.get(var0.substring(0, var1).replace('.', File.separatorChar));
Files.createDirectories(var3);
var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class");
} else {
var2 = Paths.get(var0 + ".class");
}
Files.write(var2, var4, new OpenOption[0]);
return null;
} catch (IOException var4x) {
throw new InternalError("I/O exception saving generated file: " + var4x);
}
}
});
}
return var4;
}
上面的代码,将我们传进去的三个参数重新构建了一个ProxyGenerator 对象,然后调用generateClassFile()这个方法
从名字上看,我们的.class文件应该就是通过这个方法实现的返回的也是一个字节数组var4(也是作为方法返回值)
if (saveGeneratedFiles)
{..
...
Files.write(var2, var4, new OpenOption[0]);}
往下做了这么一个判断,应该是对应了我们之前的参数设置,用来判断是否将上面的方法生成的.class保存到本地
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
接着上面的generateClassfile()这个方法中,里面应该是存在.class文件的构成逻辑(有点长)
private byte[] generateClassFile() {
this.addProxyMethod(hashCodeMethod, Object.class);
this.addProxyMethod(equalsMethod, Object.class);
this.addProxyMethod(toStringMethod, Object.class);
Class[] var1 = this.interfaces;
int var2 = var1.length;
int var3;
Class var4;
for(var3 = 0; var3 < var2; ++var3) {
var4 = var1[var3];
Method[] var5 = var4.getMethods();
int var6 = var5.length;
for(int var7 = 0; var7 < var6; ++var7) {
Method var8 = var5[var7];
this.addProxyMethod(var8, var4);
}
}
Iterator var11 = this.proxyMethods.values().iterator();
List var12;
while(var11.hasNext()) {
var12 = (List)var11.next();
checkReturnTypes(var12);
}
Iterator var15;
try {
this.methods.add(this.generateConstructor());
var11 = this.proxyMethods.values().iterator();
while(var11.hasNext()) {
var12 = (List)var11.next();
var15 = var12.iterator();
while(var15.hasNext()) {
ProxyGenerator.ProxyMethod var16 = (ProxyGenerator.ProxyMethod)var15.next();
this.fields.add(new ProxyGenerator.FieldInfo(var16.methodFieldName, "Ljava/lang/reflect/Method;", 10));
this.methods.add(var16.generateMethod());
}
}
this.methods.add(this.generateStaticInitializer());
} catch (IOException var10) {
throw new InternalError("unexpected I/O Exception", var10);
}
if (this.methods.size() > 65535) {
throw new IllegalArgumentException("method limit exceeded");
} else if (this.fields.size() > 65535) {
throw new IllegalArgumentException("field limit exceeded");
} else {
this.cp.getClass(dotToSlash(this.className));
this.cp.getClass("java/lang/reflect/Proxy");
var1 = this.interfaces;
var2 = var1.length;
for(var3 = 0; var3 < var2; ++var3) {
var4 = var1[var3];
this.cp.getClass(dotToSlash(var4.getName()));
}
this.cp.setReadOnly();
ByteArrayOutputStream var13 = new ByteArrayOutputStream();
DataOutputStream var14 = new DataOutputStream(var13);
try {
var14.writeInt(-889275714);
var14.writeShort(0);
var14.writeShort(49);
this.cp.write(var14);
var14.writeShort(this.accessFlags);
var14.writeShort(this.cp.getClass(dotToSlash(this.className)));
var14.writeShort(this.cp.getClass("java/lang/reflect/Proxy"));
var14.writeShort(this.interfaces.length);
Class[] var17 = this.interfaces;
int var18 = var17.length;
for(int var19 = 0; var19 < var18; ++var19) {
Class var22 = var17[var19];
var14.writeShort(this.cp.getClass(dotToSlash(var22.getName())));
}
var14.writeShort(this.fields.size());
var15 = this.fields.iterator();
while(var15.hasNext()) {
ProxyGenerator.FieldInfo var20 = (ProxyGenerator.FieldInfo)var15.next();
var20.write(var14);
}
var14.writeShort(this.methods.size());
var15 = this.methods.iterator();
while(var15.hasNext()) {
ProxyGenerator.MethodInfo var21 = (ProxyGenerator.MethodInfo)var15.next();
var21.write(var14);
}
var14.writeShort(0);
return var13.toByteArray();
} catch (IOException var9) {
throw new InternalError("unexpected I/O Exception", var9);
}
}
}
这么长一段代码,建议通过调试的方法进行走读,这样你还可以看到他的方法,字段是如何拼接的
通过addProxyMethod 这样的方法,将方法名,方法一一拼接,最后以字节码的形式存起来,
如法炮制,将其余的一些公共方法,诸如equal,hashcode,toString这些方法,进行add
然后就是实现接口的接口方法,我们前面说的,javajdk的动态代理是基于接口实现的,所以这里我们也是需要通过接口获取特有的方法,进行实现
通过调用接口方法,来获取方法名,进行class文件的构建
可以看到,我们的map里面已经存放了,class 文件所需要的诸多方法,这个过程其实是比较复杂的,涉及到一些反射,一些字符串处理的方式,建议有兴趣的可以去调试一下
最后,将生成代理类.class文件所需要的方法封装起来,后续还有诸如字段,异常,导入包等等的构建方法,都在ProxyGenerator这个类里面进行的,由于篇幅的原因,这里就不过多的描述(等水平提升了在进行补充)
ByteArrayOutputStream var13 = new ByteArrayOutputStream();
DataOutputStream var14 = new DataOutputStream(var13);
在将方法等 都封装完之后便开始创建输出流了
return var13.toByteArray();//最后返回给程序一个字节数组
将前面封装的属性,在这个位置输出到目的地,侧面验证了前面的猜想,因为类加载器可以通过加载目标位置的.class字节码文件,来将类加载到我们的内存中去,那么动态代理,在程序开始之前,所谓的.class字节码文件是不存在的,所以他需要先在内存缓存一份字节码输出流,用来作为类加载器的加载对象.
在走读代码的过程,偶遇会遇到一些转换的方法,是因为遇到native修饰的一些方法,java没办法直接调用,需要先转换一下,在进行调用
将拼装完的字节数组,传入我们的defineClass0方法,这个是native修饰的方法,太难了,所以先避开了,但是可以确定的是,目前我们已经可以验证大致的猜想了,defineClass0方法放回的是一个Class对象
最后返回给value
然后将value重新封装成CacheValue类型,并返回value
至此,这个类才算是加载完成,
Class<?> cl = getProxyClass0(loader, intfs);
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}
后续便是对这个加载进来的类,进行验证检查,以及创建对象,可以将最开始传入的handler那些对象传进去,构造对象,实现代理类的增强,前面那么繁杂的走读,其实就是为了构建一个可以被读取的合法的类字节码对象,用以被类加载器加载到内存中来,然后通过反射调用构造方法创建对象,并调用里面的方法,实现方法的增强…(jdk代理先到一段落)
补充:
在学习到类加载的过程的时候,前文提到的五大过程:加载,验证,准备,解析,初始化这五个过程,其中后四个构成大部分都是jvm自行解决的,唯独这个加载比较特殊,
资料显示,<<java虚拟机规范>>中并没有特别严厉的要求,所以导致这个.class的加载多样化成就了诸多java技术
手动拼接一个java类文件,然后通过java提供的编译工具将它编译成.class文件
前期准备,
- 需要被代理的类
- 代理接口
- 重写InvocationHandler
- 重写proxy
- Main调用
被代理类
package it.luke.proxy.v03_asm.Dynamic_compiler;
public class Url implements Url_Requst {
@Override
public void request() {
System.out.println("Url被请求了....");
}
}
代理接口
package it.luke.proxy.v03_asm.Dynamic_compiler;
public interface Url_Requst extends no_demo{
void request();
}
重写一个简单的InvocationHandler
package it.luke.proxy.v03_asm.Dynamic_compiler;
import java.lang.reflect.Method;
public interface InvocationHandler {
void invoke(Object o, Method method);
}
Main主类
package it.luke.proxy.v03_asm.Dynamic_compiler;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Main {
public static void main(String[] args) throws NoSuchMethodException, IOException, InstantiationException, IllegalAccessException, InvocationTargetException, ClassNotFoundException {
Url url = new Url();
Url_Requst a = (Url_Requst)Proxy.getProxyInstance(Url_Requst.class, new InvocationHandler() {
@Override
public void invoke(Object o, Method method) {
System.out.println("代理 ...");
try {
method.invoke(url);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
});
a.request();
}
}
接下来是重写Proxy,将所有的字段方法等以字符串的方式拼接在一起
package it.luke.proxy.v03_asm.Dynamic_compiler;
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.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
public class Proxy {
/**
* 通过传入的接口,和处理器进行构造
*
* @param infer
* @param invocationHandler
* @return
*/
public static Object getProxyInstance(Class infer, InvocationHandler invocationHandler) throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//构造一个java文件
//1.获取需要导入的包,构建类名
//2.获取需要的方法名,参数
//回车
String src_str = "";
String methon_str = "";
String rn = "\r\n";
Method[] methods = infer.getMethods();
for (Method m : methods) {
methon_str += "@Override" + rn +
"public void " + m.getName() + "(){" + rn +
"try { " + rn +
"Method md = " + infer.getName() + ".class.getMethod(\"" + m.getName() + "\");" + rn +
"h.invoke(this,md);" + rn +
"} catch(Exception e) {e.printStackTrace();}" + rn + "}";
}
String Package_str = infer.getPackage().getName();
src_str = "package " + Package_str +";"+ rn +
"import java.lang.reflect.*;" + rn +
"public class $Proxy1 implements "+ infer.getName()+" {" + rn +
"InvocationHandler h;" +
" public $Proxy1(InvocationHandler h ) {" + rn +
" this.h=h;" + rn +
" }" + rn +
methon_str + rn
+"}";
//it.luke.proxy.v03_asm.Dynamic_compiler
String fileName ="D:\\GitPro\\DesignPattern" +"\\src\\main\\java\\it\\luke\\proxy\\v03_asm\\Dynamic_compiler\\$Proxy1.java";
File f = new File(fileName);
FileWriter fw = new FileWriter(f);
fw.write(src_str);
fw.flush();
fw.close();
//编译 java 文件获得.class 文件
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
Iterable units = fileMgr.getJavaFileObjects(fileName);
JavaCompiler.CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units);
t.call();
fileMgr.close();
//加载生成的.class到内存
URL[] urls = {new URL("file:/D:/GitPro/DesignPattern/src/main/java/")};
URLClassLoader urlClassLoader = new URLClassLoader(urls);
Class c = urlClassLoader.loadClass("it.luke.proxy.v03_asm.Dynamic_compiler.$Proxy1");
System.out.println(c);
Constructor constructor = c.getConstructor(InvocationHandler.class);
Object o = constructor.newInstance(invocationHandler);
return o;
}
}
很直观的可以看到,我们直接将一个java文件,以字符串的形式拼接起来,然后通过编译工具getSystemJavaCompiler进行编译,然后用反射的方式进行创建对象,调用方法
结果虽然成功了,但是生成的java排版不是特别好看
package it.luke.proxy.v03_asm.Dynamic_compiler;
import java.lang.reflect.*;
public class $Proxy1 implements it.luke.proxy.v03_asm.Dynamic_compiler.Url_Requst {
InvocationHandler h; public $Proxy1(InvocationHandler h ) {
this.h=h;
}
@Override
public void request(){
try {
Method md = it.luke.proxy.v03_asm.Dynamic_compiler.Url_Requst.class.getMethod("request");
h.invoke(this,md);
} catch(Exception e) {e.printStackTrace();}
}
}
编译过后的.class文件也保存在了本地
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package it.luke.proxy.v03_asm.Dynamic_compiler;
import java.lang.reflect.Method;
public class $Proxy1 implements Url_Requst {
InvocationHandler h;
public $Proxy1(InvocationHandler var1) {
this.h = var1;
}
public void request() {
try {
Method var1 = Url_Requst.class.getMethod("request");
this.h.invoke(this, var1);
} catch (Exception var2) {
var2.printStackTrace();
}
}
}
而调用Main主类后
class it.luke.proxy.v03_asm.Dynamic_compiler.$Proxy1
代理 ...
Url被请求了....
可以看到我们成功的对被代理的方法进行了增强.
转载:https://blog.csdn.net/weixin_44445168/article/details/105905573