手写JDK Proxy实现InvocationHandler的Invoker

JDKProxy生成对象的步骤如下:

1、拿到被代理对象的引用,并且获取到它的所有的接口,反射获取。

2、JDKProxy类重新生成一个新的类、同时新的类要实现被代理类所有实现的所有的接

口。

3、动态生成Java代码,把新加的业务逻辑方法由一定的逻辑代码去调用(在代码中体

现)。

4、编译新生成的Java代码.class。

5、再重新加载到JVM中运行。

以上这个过程就叫字节码重组。JDK中有一个规范,在ClassPath下只要是$开头的class

文件一般都是自动生成的。

java 复制代码
package com.sp.demo.proxy;

/**
 * @author : lssffy
 * @Description :
 * @date : 2024/3/8 10:25
 */
public interface OneDay {

    void eat();

    void play();

    void sleep();
}
java 复制代码
package com.sp.base.proxyhandler;

import java.lang.reflect.Method;

/**
 * @author : lssffy
 * @Description :
 * @date : 2024/3/21 14:41
 */
public interface SInvocationHandler {
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}
java 复制代码
package com.sp.base.proxyhandler;

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;

/**
 * @author : lssffy
 * @Description :
 * @date : 2024/3/21 14:42
 */
public class SProxy {

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

    public static Object newProxyInstance(SClassLoader classLoader,Class<?> [] interfaces,SInvocationHandler h){
        try {
            //动态生成源代码。java文件
            String src = generateSrc(interfaces);
            //Java文件输出磁盘
            String filePath = SProxy.class.getResource("").getPath();
            File f = new File(filePath + "$Proxy0.java");
            FileWriter fw = new FileWriter(f);
            fw.write(src);
            fw.flush();
            fw.close();
            //把生成的。java文件编译成。class文件
            JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
            StandardJavaFileManager manager = compiler.getStandardFileManager(null,null,null);
            Iterable iterable = manager.getJavaFileObjects(f);
            JavaCompiler.CompilationTask task = compiler.getTask(null,manager,null,null,null,iterable);
            task.call();
            manager.close();
            //把编译生成的。class文件加载到jvm中
            Class proxyClass = classLoader.findClass("$Proxy0");
            Constructor c = proxyClass.getConstructor(SInvocationHandler.class);
            f.delete();
            //返回字节码重组以后的新的代理对象
            return c.newInstance(h);
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }

    private static String generateSrc(Class<?>[] interfaces) {
        StringBuffer sb= new StringBuffer();
        sb.append("package com.sp.base.proxyhandler;" +ln);
        sb.append("import com.sp.demo.proxy.OneDay;" + ln);
        sb.append("import java.lang.reflect.*;" +ln);
        sb.append("public class $Proxy0 implements " + interfaces[0].getName()+ "{"+ln);
        sb.append("SInvocationHandler h;"+ ln);
        sb.append("public $Proxy0(SInvocationHandler h) { "+ln);
        sb.append("this.h = h;");
        sb.append("}"+ ln);
        for(Method m : interfaces[0].getMethods()){
            Class<?>[] params= m.getParameterTypes();
            StringBuffer paramNames=new StringBuffer();
            StringBuffer paramValues=new StringBuffer();
            StringBuffer paramClasses= new StringBuffer();
            for(int i= 0;i < params.length; i++){
                Class clazz=params[i];
                String type=clazz.getName();
                String paramName= toLowerFirstCase(clazz.getSimpleName());
                paramNames.append(type + "" + paramName);
                paramValues.append(paramName);
                paramClasses.append(clazz.getName() + ".class");
                if(i > 0&& i< params.length-1){
                    paramNames.append(",");
                    paramClasses.append(",");
                    paramValues.append(",");
                }
            }
            sb.append("public " +m.getReturnType().getName() + " " + m.getName() +"(" +
                    paramNames.toString() + "){"+ ln);
            sb.append("try{"+ ln);
            sb.append("Method m="+interfaces[0].getName()+".class.getMethod(\""
                    +m.getName()+ "\",new Class[]{"+paramClasses.toString() + "});" +ln);
            sb.append((hasReturnValue(m.getReturnType()) ?"return " : "") +
                    getCaseCode("this.h.invoke(this,m,new Object[]{"+paramValues+"})",m.getReturnType())+";"+ln);
            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> mappings = new HashMap<Class,Class>();
    static {
        mappings.put(int.class,Integer.class);
    }

    private static String getReturnEmptyCode(Class<?> returnType) {
        if(mappings.containsKey(returnType)){
            return "return 0;";
        }else if(returnType ==void.class){
            return "";
        }else{
            return"return null;";
        }
    }

    private static String getCaseCode(String s, Class<?> returnType) {
        if(mappings.containsKey(returnType)){
            return "(("+mappings.get(returnType).getName() + ")"+ s+ ")."+
                    returnType.getSimpleName() +"Value()";
        }
        return s;
    }

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

    private static String toLowerFirstCase(String simpleName) {
        char [] chars = simpleName.toCharArray();
        chars[0] += 32;
        return String.valueOf(chars);
    }
}
java 复制代码
package com.sp.base.proxyhandler;

import java.io.*;

/**
 * @author : lssffy
 * @Description :
 * @date : 2024/3/21 14:44
 */
public class SClassLoader extends ClassLoader{

    private File classPathFile;

    public SClassLoader(){
        String classPath = SClassLoader.class.getResource("").getPath();
        this.classPathFile = new File(classPath);
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        String className = SClassLoader.class.getPackage().getName() + "." + name;
        if (classPathFile != null) {
            File classFile = new File(classPathFile,name.replace("\\", "/") + ".class");
            if(classFile.exists()) {
                FileInputStream fis = null;
                ByteArrayOutputStream out = null;

                try {
                    fis = new FileInputStream(classFile);
                    out = new ByteArrayOutputStream();
                    byte [] buff = new byte[1024];
                    int len;
                    while((len=fis.read(buff))!=-1){
                        out.write(buff,0,len);
                    }
                    return defineClass(className,out.toByteArray(),0,out.size());
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }finally {
                    if(null!=fis){
                        try {
                            fis.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                    if(null!=out){
                        try {
                            out.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
        return null;
    }
}
java 复制代码
package com.sp.demo.proxy;

import com.sp.base.proxyhandler.SClassLoader;
import com.sp.base.proxyhandler.SInvocationHandler;
import com.sp.base.proxyhandler.SProxy;

import java.lang.reflect.Method;

/**
 * @author : lssffy
 * @Description :
 * @date : 2024/3/21 14:15
 */
public class InvocationHandlerJDK implements SInvocationHandler {

    private Object target;

    public Object getInstance(Object target) {
        this.target = target;
        Class<?> targetClass = target.getClass();
        return SProxy.newProxyInstance(new SClassLoader(),targetClass.getInterfaces(),this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();
        Object obj = method.invoke(this.target, args);
        after();
        return obj;
    }

    private void before(){
        System.out.println("start runtime invocation");
    }

    private void after(){
        System.out.println("end runtime invocation");
    }


}
java 复制代码
package com.sp.demo.proxy;

/**
 * @author : lssffy
 * @Description :
 * @date : 2024/3/8 10:26
 */
public class LaoWang implements OneDay{
    @Override
    public void eat() {
        System.out.println("准备碗筷");
        System.out.println("开始吃饭");
        System.out.println("收拾碗筷");
    }

    @Override
    public void play() {
        System.out.println("打开电脑");
        System.out.println("登录账号");
        System.out.println("开始玩");
    }

    @Override
    public void sleep() {
        System.out.println("开始洗澡");
        System.out.println("躺倒床上");
        System.out.println("开始睡觉");
    }
}
java 复制代码
package com.sp.demo.proxy;

/**
 * @author : lssffy
 * @Description :
 * @date : 2024/3/8 10:33
 */
public class Test {

    public static void main(String[] args) {
        try {
            OneDay oneDay1 = (OneDay) new InvocationHandlerJDK().getInstance(new LaoWang());
            oneDay1.play();
            oneDay1.eat();
            oneDay1.sleep();
        }catch(Exception e){
            e.printStackTrace();
        }
    }
}
相关推荐
weixin_462428478 分钟前
使用 Caffeine 缓存并在业务方法上通过注解实现每3到5秒更新缓存
java·缓存
程序媛小果10 分钟前
基于java+SpringBoot+Vue的桂林旅游景点导游平台设计与实现
java·vue.js·spring boot
骑鱼过海的猫12311 分钟前
【java】java通过s3访问ceph报错
java·ceph·iphone
杨充17 分钟前
13.观察者模式设计思想
java·redis·观察者模式
Lizhihao_20 分钟前
JAVA-队列
java·开发语言
喵叔哟29 分钟前
重构代码之移动字段
java·数据库·重构
喵叔哟29 分钟前
重构代码之取消临时字段
java·前端·重构
fa_lsyk31 分钟前
maven环境搭建
java·maven
远望清一色38 分钟前
基于MATLAB边缘检测博文
开发语言·算法·matlab
何曾参静谧1 小时前
「Py」Python基础篇 之 Python都可以做哪些自动化?
开发语言·python·自动化