自定义动态代理模式

前言

上一节中我们简单概述了JDK中的动态代理模式,但是难免心中存疑,我们可以很明确的知道最核心的地方就是生成代理类的过程,也就是我们的这一块代码:Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this) 针对于这个点,我们来说一下在JDK的动态代理模式中所存在的疑问:

  • 1.这个clazz.getClassLoader是什么东西
  • 2.这个clazz.getInterfaces()是什么东西
  • 3.以及最后我们所传递的this(其实是一个InvocationHandler对象)这三个东西究竟是什么

我们这看到了两个比较重要的东西

  • 1.this对象,在前一节中我们在阅读JavaDoc的时候就已经发现了,InvocationHandler这个对象非常重要
  • 2.众所周知,类加载器是一个神气的东西,我在我的博客中Springboot启动方式中,已经说明了,在不违反规定的前提下,打破Jar in Jar的界限。。说的有点高大上了,我有点不好意思了,咳咳,就是去寻找我们的.class文件

这次,我们来重点实现下自定义InvocationHandler和类加载器 ,下面我们开始来看代码

下面我们开始看代码

Person类


/**
 * @author Zerox
 * @date 2019/6/1 19:23
 * 首先这是一个接口,为了干什么呢?给广大单身男性提供方便的
 */
public interface Person {

    /**
     * 寻找真爱,
     */
    public void findTrueLove();

}

接口实现类 —> XiaoWang


/**
 * @author Zerox
 * @date 2019/6/1 19:34
 */
public class XiaoWang implements Person {

    private String name = "小王";

    private String sex = "男";
    /**
     * 小王寻找真爱
     */
    public void findTrueLove() {

        System.out.println("我的名字是" + this.name +  ",性别是:" + this.sex);
        System.out.println("我想要做的事情是寻找真爱!" );
        System.out.println("我的要求是: 肤白貌美大长腿");
    }
}

下面我们来实现自定义的MeiPo类


/**
 * @author Zerox
 * @date 2019/6/2 13:16
 */
public class ConsumerMeiPo implements ConsumerInvocationHandler{

    // 获取被代理的对象
    private Person xiaoWang;

    // 生成xiaoWang的代理对象
    public Object getProxyInstance(Person xiaoWang) throws Exception {

        // 赋值
        this.xiaoWang = xiaoWang;
        // 获取类对象
        Class<?> clazz = xiaoWang.getClass();

        // 返回代理对象
        return ConsumerProxy.newProxyInstance(new ConsumerClassLoader("myConsumerClassLoader"),clazz.getInterfaces(),this);
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("开始进行海选");
        System.out.println("---------------");
        method.invoke(this.xiaoWang,args);
        System.out.println("如果合适就准备办事!");
        return null;
    }
}

下面我们来实现自定义的ConsumerProxy类



/**
 * @author Zerox
 * @date 2019/6/3 9:34
 */
public class ConsumerProxy {


    private static String ln = "\r\n";


    public static Object newProxyInstance(ConsumerClassLoader loader, Class<?>[] interfaces, ConsumerInvocationHandler h)
            throws Exception {


        // 1. 生成源代码
        String proxySrc = generateSrc(interfaces[0]);

        // 2. 将生成的源代码输出到磁盘,保存为java文件
            // 2.1 文件路径
            String filePath = ConsumerProxy.class.getResource("").getPath();
            // 2.2 将文件转换成byte[]字节数组,理论上IO的最大传输效率
            byte[] data = new byte[1024*8];
            // 2.3 将文件写入本地
            data = proxySrc.getBytes();

            File file = new File(filePath + "$Proxy0.java");
            FileOutputStream fos = new FileOutputStream(file);
            int len = 0;
            fos.write(data);
            fos.flush();
            fos.close();
        // 3. 编译源代码,并生成.class文件
         JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
         StandardJavaFileManager manager = compiler.getStandardFileManager(null,null,null);
         Iterable iterable = manager.getJavaFileObjects(file);
         JavaCompiler.CompilationTask task = compiler.getTask(null,manager,null,null,null,iterable);
         task.call();
         manager.close();

        //4. 将.class文件中的内容,动态加载到JVM中
        Class proxyClass = loader.findClass("$Proxy0");
        //5. 返回被代理后的对象
        Constructor c = proxyClass.getConstructor(ConsumerInvocationHandler.class);
        file.delete();
        return c.newInstance(h);



    }

    /**
     * 用代码生成源代码
     * @param interfaces
     * @return
     */
    private static String generateSrc( Class<?> interfaces) {

        StringBuilder src = new StringBuilder();
        src.append("package blog.consumer;" + ln);
        src.append("import java.lang.reflect.Method;" + ln);
        src.append("public class $Proxy0 implements "+ interfaces.getName() + "{" + ln); //

        src.append("ConsumerInvocationHandler h;" + ln);

        src.append("public $Proxy0(ConsumerInvocationHandler h) {" + ln);
        src.append("this.h = h;" + ln);
        src.append("}" + ln );

        for(Method m:interfaces.getMethods()) {
            src.append("public " + m.getReturnType().getName() + " " + m.getName() + "(){" + ln);
            src.append("try{" + ln);
            src.append("Method m = " + interfaces.getName() + ".class.getMethod(\""+m.getName() +"\",new Class[]{});" + ln);
            src.append("this.h.invoke(this,m,null);" + ln);
            src.append("}catch(Throwable ex){" + ln);
            src.append("ex.printStackTrace();" + ln);
            src.append("}" + ln);
            src.append("}" + ln);
        }

        src.append("}");


        return src.toString();
    }
}

下面我们来实现自定义的ConsumerInvocationHandler接口


/**
 * @author Zerox
 * @date 2019/6/2 13:16
 */
public interface ConsumerInvocationHandler {

    /**
     *  实现接口里面的方法
     * @param proxy   代理对象,这里面其实就是我们的ConsumerMeiPo
     * @param method  代理对象实例重新编码以及重新分配的方法,参考上一讲中的super.h.invoke()
     * @param args    代理实体方法里面的参数(在我们这里,暂时没啥用,我也没用到)
     * @return
     * @throws Throwable
     */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}

下面我们来实现自定义的ConsumerClassLoader接口


/**
 * @author Zerox
 * @date 2019/6/2 13:24
 */
public class ConsumerClassLoader extends ClassLoader{

    private String classLoaderName;

    private final String fileExtension = ".class";

    public ConsumerClassLoader(ClassLoader parent, String classLoaderName) {
        super(parent); // 显示指定该类加载器的父类加载器
        this.classLoaderName = classLoaderName;
    }

    public ConsumerClassLoader(String classLoaderName) {
        super(); // 将系统类加载器(AppClassLoader)当做该类的父加载器
        this.classLoaderName = classLoaderName;
    }

    /**
     * 加载类对象,我们将我们所学的JVM和自定义类加载器关联到一起吧
     * @param className
     * @return
     * @throws ClassNotFoundException
     */
    @Override
    protected Class<?> findClass(String className) throws ClassNotFoundException {


        byte[] data = this.loadClassData(className);
        className = ConsumerClassLoader.class.getPackage().getName() + "." + className.replace("/",".");

        return this.defineClass(className,data,0,data.length);
    }


    public byte[] loadClassData(String name) {


        InputStream is = null;
        byte[] data = null;

        ByteArrayOutputStream baos = null;

        
        try{
        String filePath = ConsumerClassLoader.class.getResource("").getPath();
        File file = new File(filePath+""+name + this.fileExtension);
        is = new FileInputStream(file);

        baos = new ByteArrayOutputStream();
        byte[] buff = new byte[1024];
        int ch = 0;

        while(-1 !=(ch = is.read(buff))) {

            baos.write(buff,0,ch);
        }

        data = baos.toByteArray();

        }catch (Exception ex) {
            ex.printStackTrace();
        }finally {

            try{
            is.close();
            baos.close();
            }catch (Exception ex) {
                ex.printStackTrace();
            }
        }
        return data;
    }
}

输出结果如下图:

VJWrwT.png

具体的实现都在代码里面,不懂得留言,欢迎交流!

发布了32 篇原创文章 · 获赞 26 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/qq_27416233/article/details/90754586
今日推荐