学习笔记-Java代理模式的动态代理的原理理解

在学习代理模式的过程中,特别是调用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没办法直接调用,需要先转换一下,在进行调用

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-S0NpismQ-1588496197776)(类加载器.assets/1588494108361.png)]

将拼装完的字节数组,传入我们的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文件
前期准备,

  1. 需要被代理的类
  2. 代理接口
  3. 重写InvocationHandler
  4. 重写proxy
  5. 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被请求了....

可以看到我们成功的对被代理的方法进行了增强.

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值