首页 > 其他分享 >JDK动态代理如何实现

JDK动态代理如何实现

时间:2023-12-19 17:01:12浏览次数:22  
标签:return JDK 代理 public new sb 动态 class append

通过字节重组,重新生成对象来代替原始对象,以达到代理的目的。

字节码重组的基本步骤如下:

1.获取被代理对象的引用,利用反射获取到它的所有接口。

2.JDK动态代理类Proxy重新生成一个新的类,此类要实现刚才获取到的所有接口。

3.动态生成新类的Java代码。

4.编译.java文件成.class文件。

5.加载编译好的.class文件。

创建Person接口:

public interface Person {
    void sing();
}

创建Customer类:

public class Customer implements Person{

    @Override
    public void sing() {
        System.out.println("唱的很好听");
    }
}

创建MyInvocationHandler接口:

import java.lang.reflect.Method;

public interface MyInvocationHandler {
    Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}

自定义类加载器MyClassLoader:

import java.io.*;

public class MyClassLoader extends ClassLoader {

    private File classPathFile;

    public MyClassLoader() {
        //class文件路径
        String classPath = MyClassLoader.class.getResource("").getPath();
        this.classPathFile = new File(classPath);
    }

    /**
     * 寻找需要加载的class文件并返回类对象
     *
     * @param name 类名称
     * @return 找到Class对象
     * @throws ClassNotFoundException
     */
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        String className = MyClassLoader.class.getPackage().getName() + "." + name;

        if (null != classPathFile) {
            File classFile = new File(classPathFile, name + ".class");
            if (classFile.exists()) {
                FileInputStream fis = null;
                ByteArrayOutputStream baos = null;

                try {
                    fis = new FileInputStream(classFile);
                    baos = new ByteArrayOutputStream();

                    byte[] buff = new byte[1024];
                    int len;

                    while ((len = fis.read(buff)) != -1){
                        baos.write(buff,0,len);
                    }

                    return defineClass(className,baos.toByteArray(),0,baos.size());

                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    if (null != fis) {
                        try {
                            fis.close();

                            if (null != baos) {
                                baos.close();
                            }
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
        return null;
    }
}

自定义Proxy类MyProxy:

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.util.HashMap;
import java.util.Map;

public class MyProxy {

    public static final String ln = "\r\n";

    public static Object newMyProxyInstance(MyClassLoader classLoader, Class<?>[] interfaces, MyInvocationHandler handler) {

        try {

            String src = generateSrc(interfaces);
            //将Java文件输出到磁盘上
            String filePath = MyProxy.class.getResource("").getPath() + "$Proxy0.java";
            File file = new File(filePath);
            FileWriter fw = new FileWriter(file);
            fw.write(src);
            fw.flush();
            fw.close();

            //编译Java文件成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();

            //加载类
            Class proxyClass = classLoader.findClass("$Proxy0");
            Constructor constructor = proxyClass.getConstructor(MyInvocationHandler.class);
            file.delete();

            return constructor.newInstance(handler);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }


    private static String generateSrc(Class<?>[] interfaces) {

        StringBuffer sb = new StringBuffer();

        sb.append("package com.study.demo.proxy2;" + ln);
        sb.append("import com.study.demo.proxy2.Person;" + ln);
        sb.append("import java.lang.reflect.*;" + ln);
        sb.append("public class $Proxy0 implements " + interfaces[0].getName() + "{" + ln);
        sb.append("MyInvocationHandler handler;" + ln);
        sb.append("public $Proxy0(MyInvocationHandler handler) {" + ln);
        sb.append("this.handler = handler;");
        sb.append("}" + ln);


        for (Method m : interfaces[0].getMethods()) {
            //获得每个方法的形式参数
            Class<?>[] params = m.getParameterTypes();
            StringBuffer paramNames = new StringBuffer();
            StringBuffer paramValus = new StringBuffer();
            StringBuffer paramClasses = new StringBuffer();

            for (int i = 0; i < params.length; i++) {
                Class clazz = params[i];
                String type = clazz.getName();
                //将类写名称第一个字母小写order
                String paramName = toLowerFirstCase(clazz.getSimpleName());

                paramNames.append(type + " " + paramName);
                paramValus.append(paramName);
                //cn.xupt.design_pattern.structure.proxy.handwriting_dynamic_proxy.Order.class
                paramClasses.append(clazz.getName() + ".class");

                if (i > 0 && i < params.length - 1) {
                    paramNames.append(",");
                    paramClasses.append(",");
                    paramValus.append(",");
                }

            }

            //构造各个方法
            sb.append("public " + m.getReturnType().getName() + " " + m.getName() + " " + "(" +
                    paramNames.toString() + "){" + ln);
            sb.append("try{" + ln);
            sb.append("Method method = " + interfaces[0].getName() + ".class.getMethod(\"" + m.getName()
                    + "\", new Class[]{" + paramClasses.toString() + "});" + ln);
            sb.append((hasReturnValue(m.getReturnType()) ? "return " : "") + getCaseCode(
                    "this.handler.invoke(this, method, new Object[]{" + paramValus + "})", m.getReturnType()) + ";");
            sb.append("}catch(Error _ex){}");
            sb.append("catch(Throwable e){" + ln);
            sb.append("throw new UndeclaredThrowableException(e);" + ln);
            sb.append("}");
            sb.append(getReturnEmptyCode(m.getReturnType()));
            sb.append("}");
        }
        sb.append("}" + ln);
        return sb.toString();
    }

    private static Map<Class, Class> mapping = new HashMap<Class, Class>();

    static {
        mapping.put(int.class, Integer.class);
    }

    private static String getReturnEmptyCode(Class<?> returnClass) {

        if (mapping.containsKey(returnClass)) {
            return "0";
        } else if (returnClass == void.class) {
            return "";
        } else {
            return "return null";
        }
    }

    private static String getCaseCode(String code, Class<?> returnClass) {

        if (mapping.containsKey(returnClass)) {
            return "((" + mapping.get(returnClass).getName() + ")" + code + ")." + returnClass.getSimpleName() + "Value()";
        }
        return code;
    }

    private static boolean hasReturnValue(Class<?> clazz) {
        return clazz != void.class;
    }

    private static String toLowerFirstCase(String src) {

        char[] chars = src.toCharArray();
        chars[0] += 32;
        return String.valueOf(chars);
    }
}

创建歌手类Singer:

import java.lang.reflect.Method;

public class Singer implements MyInvocationHandler{

    private Object target;

    public Object getInstance(Object target) {
        this.target = target;
        Class<?> aClass = target.getClass();
        return MyProxy.newMyProxyInstance(new MyClassLoader(), aClass.getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        before();
        Object invoke = method.invoke(this.target, args);
        after();
        return invoke;
    }

    private void before() {
        System.out.println("我是歌手");
    }

    private void after() {
        System.out.println("休息!");
    }

}

创建Client类

public class Test {

    public static void main(String[] args) {
        Person person = (Person) new Singer().getInstance(new Customer());
        person.sing();
    }
}

标签:return,JDK,代理,public,new,sb,动态,class,append
From: https://blog.51cto.com/u_11315052/8891252

相关文章

  • nginx反向代理jumpserver
    背景之前的文章介绍了,如何使用docker部署jumpserver当需要配置成https的时候,会发现很不好调整那么就出现了一种新的场景,那就是,后端服务不能动,如何在原来的基础上实现https的方式访问思路通过nginx反向代理给后端实现jumpserver的web服务地址是10.0.0.12:8888生成证书这......
  • 如何建立自己的代理IP池,减少爬虫被封的几率
    前言建立自己的代理IP池可以帮助减少爬虫被封的几率。通过使用代理IP,我们可以隐藏爬虫的真实IP地址,提高爬取网站的稳定性和安全性。本文将介绍如何建立一个代理IP池,并提供相关代码示例。一、了解代理IP的工作原理在开始建立代理IP池之前,我们需要了解代理IP的工作原理。代理IP是一个......
  • 动态表单如何校验?
    记录一下,直接上代码,核心代码就一行。注:prop需要定位到表单项的值,如下定义为[${index}].value,即校验canConfigList[${index}].value的值<el-form:inline="true"ref="addChannelRef":model="canConfigList"label-width="110px"><e......
  • Ubuntu安装openjdk8
    安装openjdk,做简单记录新服务器安装java环境:(1)更新软件源sudoapt-getupdate(2)安装openjdk8aptinstall openjdk-8-jdk//下面不需要,了解下。#修改环境变量vim/etc/profilesource/etc/profile 是一个shell命令,用于重新加载系统环境变量文件 /etc/profile,以便使修......
  • layui 时间控件 动态js渲染添加
    <tableclass="layui-table"id="myTable"><thead><tr><th>日期</th><th>操作</th></tr></thead><tbody><tr><td>......
  • 35. 干货系列从零用Rust编写负载均衡及代理,代理服务器的源码升级改造
    wmproxywmproxy已用Rust实现http/https代理,socks5代理,反向代理,静态文件服务器,四层TCP/UDP转发,七层负载均衡,内网穿透,后续将实现websocket代理等,会将实现过程分享出来,感兴趣的可以一起造个轮子项目地址国内:https://gitee.com/tickbh/wmproxygithub:https://github.com/......
  • docker安装openjdk并运行jar包的操作方法
    下载镜像dockerpullopenjdk创建数据卷创建一个java_app的数据卷dockervolumecreatejava_app将jar包上传到/var/lib/docker/volumes/java_app/_data/下,然后启动镜像启动镜像dockerrun--name=javaApp--restart=always--network=host\-vjava_app:/usr/src/m......
  • 如何实现前端跨域,配置代理
    //在vue.config.js中配置代理const{defineConfig}=require('@vue/cli-service')module.exports=defineConfig({ transpileDependencies:true,  devServer:{  host:"localhost",  port:8080,//端口号  https:false,//https:{type:B......
  • Nginx反向代理
    参考:https://mp.weixin.qq.com/s/2QVkA0ViNkO_i7fiZtIknQ反向代理是Nginx作为Web服务器最常用的功能之一。什么是反向代理呢?很多初学者在第一次遇到这个名词的时候总免不了出现很多问号。举个例子,小二的浏览器是无法直接访问谷哥的,但香港的代理服务器是可以访问谷哥的,于是小......
  • RK3568 android12 动态替换开机logo
    前言:最近客户有个需要,通过adbpush来动态替换开机logo。通过网上查阅相关资料,现整理如下。参考:RK3568Android/Linux系统动态更换U-Boot/KernelLogo解决方法:通过自定义一个分区来存储开机logo,这样在恢复出厂时不会丢失开机logo。然后通过修改u-boot/drivers/video/drm/rock......