Java基础 代理模式 实现JDK动态代理

本文从0开始实现JDK动态代理功能,代理模式更多扩展,请参考此链接Java基础 代理模式

JDK动态代理应用

JDK动态代理角色划分

业务逻辑接口类:需要被代理的接口类。
业务逻辑类:具体业务逻辑代码(增删改查)。
代理逻辑接口类:JDK提供的InvocationHandler接口。
代理逻辑类:具体业务逻辑代码(修改目标方法)。
代理生成类:JDK提供的Proxy类。

实体类User
public class User {
    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }
    private String name;
    private int age;
}
业务逻辑接口类IUserService
public interface IUserService {
    public int insertUser(User user) throws Throwable;
}
业务逻辑类UserService
public class UserService implements IUserService {
    @Override
    public int insertUser(User user) {
        System.out.println("假装访问数据库,插入一条User数据");
        return 1;
    }
}
代理逻辑类IUserServiceInvocationHandler
public class IUserServiceInvocationHandler implements InvocationHandler {
    private Object target;

    public IUserServiceInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("JDK动态代理:调用IUserServiceInvocationHandler.invoke方法");
        return method.invoke(target, args);
    }
}
启动类Application
public class Application {
    public static void main(String[] args) throws Throwable {
        User user = new User("tbryant", 18);
        // JDK动态代理
        System.out.println("JDK动态代理demo:");
        IUserService jdkProxy = (IUserService) Proxy.newProxyInstance(Application.class.getClassLoader(),
                new Class[]{IUserService.class}, new IUserServiceInvocationHandler(new UserService()));
        jdkProxy.insertUser(user);

        // 自定义动态代理
        System.out.println("自定义动态代理demo:");
        IUserService customProxy = (IUserService) CustomProxy.newProxyInstance(Application.class.getClassLoader(),
                new Class[]{IUserService.class}, new IUserServiceCustomInvocationHandler(new UserService()));
        customProxy.insertUser(user);
    }
}
执行结果

在这里插入图片描述
为了方便对比,我把JDK动态代理和自定义动态代理的调用代码和执行结果贴在一起。

自定义动态代理

JDK动态代理InvocationHandler接口对应自定义动态代理CustomInvocationHandler接口,invoke方法签名保持一致。
JDK动态代理Proxy类对应自定义动态代理CustomProxy类,newProxyInstance方法签名保持一致。
业务逻辑接口类IUserService和业务逻辑类UserService代码不变。

代理逻辑接口类CustomInvocationHandler
public interface CustomInvocationHandler {
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}
代理逻辑类IUserServiceCustomInvocationHandler
public class IUserServiceCustomInvocationHandler implements CustomInvocationHandler {
    private Object target;

    public IUserServiceCustomInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("自定义动态代理:调用IUserServiceCustomInvocationHandler.invoke方法");
        return method.invoke(target, args);
    }
}
代理生成类CustomProxy
/**
 * 模拟JDK动态代理工具类
 */
public class CustomProxy {
    /**
     * 根据目标对象进行分析,得到代理对象代码,拼接成string
     * 保存到磁盘得到.java文件
     * 编译.java文件得到.class文件
     * 把.class文件load到内存
     * 反射得到proxy对象
     */
    public static Object newProxyInstance(ClassLoader loader, Class[] interfaces, CustomInvocationHandler h) {
        Object proxy = null;
        // 第一步:根据目标对象进行分析,得到代理对象代码,拼接成string
        String line = "\n";// 换行
        String tab = "\t";// 缩进
        Class<?> targetInterface = interfaces[0];// TODO 先实现代理一个接口,后续实现代理多个接口
        String targetInterfaceFullName = targetInterface.getName();// 接口全限定名
        String targetInterfaceName = targetInterface.getSimpleName();// 接口名

        String content = "";
        String packageContent = "package com.tbryant;" + line;
        String importContent = "import " + targetInterfaceFullName + ";" + line
                + "import com.tbryant.customjdkproxy.proxy.CustomInvocationHandler;" + line
                + "import java.lang.reflect.Method;" + line;

        String clazzContent = "public class $CustomProxy implements " + targetInterfaceName + " {" + line;
        String filedContent = tab + "private CustomInvocationHandler h;" + line;
        String constructorContent = tab + "public $CustomProxy(CustomInvocationHandler h){" + line
                + tab + tab + "this.h=h;" + line
                + tab + "}" + line;
        String methodContent = "";
        Method[] methods = targetInterface.getDeclaredMethods();
        for (Method method : methods) {
            Class[] args = method.getParameterTypes();// 方法参数
            // 循环取方法参数类型,拼接方法参数字符串
            String argsTypesContent = "";// 参数类型 String,String
            String argsTypesAndArgsContent = "";// 参数签名 String arg0,String arg1
            String argsContent = "";// 参数 arg0,arg1
            int i = 0;
            for (Class arg : args) {
                String argTypeFullName = arg.getName();
                importContent += "import " + argTypeFullName + ";" + line;
                String argTypeName = arg.getSimpleName();
                argsTypesContent += argTypeName + ".class,";
                argsTypesAndArgsContent += argTypeName + " arg" + i + ",";
                argsContent += "arg" + i + ",";
                i++;
            }
            if (argsTypesContent.length() > 0) {// 有参数的话,去掉末尾逗号
                argsTypesContent = argsTypesContent.substring(0, argsTypesContent.lastIndexOf(","));
                argsTypesAndArgsContent = argsTypesAndArgsContent.substring(0, argsTypesAndArgsContent.lastIndexOf(","));
                argsContent = argsContent.substring(0, argsContent.lastIndexOf(","));
            }
            String methodName = method.getName();// 方法名
            String returnTypeName = method.getReturnType().getSimpleName();// 方法返回值类型名字
            if ("".equals(argsTypesContent)) {
                argsTypesContent += "(Class<?>[]) null";
            }
            if ("void".equals(returnTypeName)) {// 判断是否有返回值
                methodContent += tab + "public " + returnTypeName + " " + methodName + "(" + argsTypesAndArgsContent + ") throws Throwable {" + line
                        + tab + tab + "Method method=Class.forName(\"" + targetInterfaceFullName + "\").getDeclaredMethod(\"" + methodName + "\"," + argsTypesContent + ");" + line
                        + tab + tab + "h.invoke(null,method,new Object[]{" + argsContent + "});" + line
                        + tab + "}" + line;
            } else {
                methodContent += tab + "public " + returnTypeName + " " + methodName + "(" + argsTypesAndArgsContent + ") throws Throwable {" + line
                        + tab + tab + "Method method=Class.forName(\"" + targetInterfaceFullName + "\").getDeclaredMethod(\"" + methodName + "\"," + argsTypesContent + ");" + line
                        + tab + tab + "return (" + returnTypeName + ")h.invoke(null,method,new Object[]{" + argsContent + "});" + line
                        + tab + "}" + line;
            }
        }
        content = packageContent + importContent + clazzContent + filedContent + constructorContent + methodContent + "}";

        // 第二步:保存到磁盘得到.java文件
        File file = new File("d:\\com\\tbryant\\$CustomProxy.java");
        try {
            if (!file.exists()) {
                file.createNewFile();
            }
            FileWriter fw = new FileWriter(file);
            fw.write(content);
            fw.flush();
            fw.close();

            // 第三步:编译.java文件得到.class文件
            JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
            StandardJavaFileManager standardJavaFileManager = javaCompiler.getStandardFileManager(null, null, null);
            Iterable javaFileObjects = standardJavaFileManager.getJavaFileObjects(file);
            JavaCompiler.CompilationTask compilationTask = javaCompiler.getTask(null, standardJavaFileManager, null, null, null, javaFileObjects);
            compilationTask.call();
            standardJavaFileManager.close();

            // 第四步:把.class文件load到内存
            URL urls[] = new URL[]{new URL("file:D:\\\\")};
            URLClassLoader urlClassLoader = new URLClassLoader(urls);
            Class clazz = urlClassLoader.loadClass("com.tbryant.$CustomProxy");

            // 第五步:反射得到proxy对象
            Constructor constructor = clazz.getConstructor(CustomInvocationHandler.class);
            proxy = constructor.newInstance(h);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return proxy;
    }
}

源码地址:CustomJDKProxy模块

发布了14 篇原创文章 · 获赞 3 · 访问量 874

猜你喜欢

转载自blog.csdn.net/qq_37956177/article/details/103550664