手写JDK动态代理并简易实现Spring的各类通知

实现原理是在本地生成一个.java文件,再使用jdk自带的javax.tools.JavaCompiler类在程序运行过程中编译运行生成的java文件,加载到内存中。

MyProxy.java

import javax.tools.JavaCompiler;
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.lang.reflect.Parameter;
import java.net.URL;
import java.net.URLClassLoader;

public class MyProxy {
    public static Object newMyProxyInstance(Class clazz, MyInvocationHandler myInvocationHandler) {
        String methodContext = "";
        String rn = "\r\n";
        String importContext = clazz.getName().replace("." + clazz.getSimpleName(), "");
        for (Method method : clazz.getMethods()) {
            String parameterContext = "", argsContext = "", paramTypeContext = "";
            for (Parameter parameter : method.getParameters()) {
                argsContext += parameter.getName() + ",";
                paramTypeContext += parameter.getType().getName() + ".class,";
                parameterContext += parameter.getType().getSimpleName() + " " + parameter.getName() + ",";
            }
            if (parameterContext.length() > 0) {
                argsContext = argsContext.substring(0, argsContext.length() - 1);
                parameterContext = parameterContext.substring(0, parameterContext.length() - 1);
                paramTypeContext = paramTypeContext.substring(0, paramTypeContext.length() - 1);
            }
            methodContext += rn + "\t@Override" + rn
                    + "\tpublic " + method.getReturnType().getSimpleName() + " " + method.getName() + "(" + parameterContext + "){" + rn
                    + "\t\ttry {" + rn
                    + "\t\t\tMethod method = " + clazz.getName() + ".class.getMethod(\"" + method.getName() + "\"" + "," + paramTypeContext + ");" + rn
                    + "\t\t\treturn (" + method.getReturnType().getSimpleName() + ") myInvocationHandler.call(this,method," + argsContext + ");" + rn
                    + "\t\t}catch(Exception e) {" + rn
                    + "\t\t\te.printStackTrace();" + rn
                    + "\t\t}" + rn
                    + "\t\treturn null;" + rn
                    + "\t}" + rn;
        }
        String context = "package " + importContext + ";" + rn + rn
                + "import java.lang.reflect.Method;" + rn + rn
                + "public class $Proxy0 implements " + clazz.getName() + "{" + rn + rn
                + "\tcom.myproxy.MyInvocationHandler myInvocationHandler;" + rn + rn
                + "\tpublic $Proxy0(com.myproxy.MyInvocationHandler myInvocationHandler) {" + rn
                + "\t\tthis.myInvocationHandler = myInvocationHandler;" + rn
                + "\t}" + rn
                + methodContext + rn
                + "}";
        try {
            String fileName = clazz.getResource("/").getPath() + importContext.replaceAll("\\.","/") + "/$Proxy0.java";
            fileName = fileName.replaceAll("%20", " ");
            File file = new File(fileName);
            if (!file.getParentFile().exists()) {
                file.getParentFile().mkdirs();
            }
            if (!file.exists()) {
                file.createNewFile();
            }
            FileWriter fw = new FileWriter(file);
            fw.write(context);
            fw.flush();
            fw.close();
            JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
            StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
            Iterable units = fileMgr.getJavaFileObjects(fileName);
            JavaCompiler.CompilationTask task = compiler.getTask(null, fileMgr, null, null, null, units);
            task.call();
            fileMgr.close();
            URL[] urls = new URL[]{new URL("file:/" + fileName)};
            URLClassLoader ul = new URLClassLoader(urls);
            Constructor constructor = ul.loadClass(importContext + ".$Proxy0").getConstructor(MyInvocationHandler.class);
            return constructor.newInstance(myInvocationHandler);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return null;
    }
}

MyInvocationHandler.java

public abstract class MyInvocationHandler {

    protected Object t;

    public <T, E> E getProxy(T t, Class<E> clazz) {
        this.t = t;
        return (E) MyProxy.newMyProxyInstance(clazz, this);
    }

    public Object call(Object proxy, Method method, Object... args) {
        Object returnContext = null;
        try {
            before();
            before(method);
            returnContext = method.invoke(t, args);
            afterReturning();
            afterReturning(returnContext);
        } catch (Throwable throwable) {
            afterThrowing();
            afterThrowing(throwable);
        } finally {
            after();
        }
        return returnContext;
    }

    protected void before(){}

    protected void before(Method method){}

    protected void after(){}

    protected void afterReturning(){}

    protected void afterReturning(Object returnContext){}

    protected void afterThrowing(){}

    protected void afterThrowing(Throwable throwable){}

}

MyHandler.java

public class MyHandler extends MyInvocationHandler {

    @Override
    public void before() {
        System.out.println("支付前");
    }

    @Override
    public void after() {
        System.out.println("支付后");
    }

    @Override
    public void afterReturning() {
        System.out.println("支付成功");
    }

    @Override
    public void afterThrowing() {
        System.out.println("出异常了");
    }

}

测试方法

    @Test
    public void test06() {
        Payment proxy = new MyHandler().getProxy(new Payment() {
            @Override
            public String pay(double price) {
                return "支付了" + price;
            }

            @Override
            public String rest(double rest) {
                return "还剩" + rest;
            }
        }, Payment.class);
        System.out.println(proxy.pay(15.6));
        System.out.println(proxy.rest(15.7));
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值