精讲23种设计模式-001:纯手写Java动态代理模式

1 代理设计模式基本概念与应用场景

课程内容

  1. 代理模式为何是程序员必备技能
  2. 静态代理与动态代理实现区别
  3. Jdk动态代理为何需要InvocationHandler接口
  4. CGLIB动态代理与Jdk动态代理区别
  5. 10分钟完全纯手写一个Jdk动态代理

代理设计模式
代理设计模式主要对方法前后实现增强

应用场景
1 打印日志
2 AOP底层通过代理模式封装
3 事务@Transactional 底层AOP
4 Mybatis中的mapper接口
5 全局捕获异常
6 Lcn、Seata、分表分库框架sharding-jdbc 代理数据源
7 自定义注解生效
8 Rpc远程调用技术(通过dubbo调用接口只会引入接口)

代理模式优缺点
优点:实现扩展功能、对方法实现增强、安全性、解决代码冗余提高代码复用机制
缺点:生成非常多的class文件(代理类)

代理模式的分类
静态代理与动态代理

代理模式实现的原理
代理模式主要包含三个角色,即抽象主题角色(Subject)、委托类角色(被代理角色,Proxied)以及代理类角色(Proxy),如图所示:
请添加图片描述
抽象主题角色:可以是接口,也可以是抽象类;
委托类(被代理类)角色:真实主题角色,业务逻辑的具体执行者;
代理类角色:内部含有对真实对象的引用,负责对真实主题角色的调用,并在真实主题角色处理前后做预处理和后处理。
eg:买房 中介 卖房(被代理类)

2 实现静态代理两种方式

静态代理的实现方式
基于接口实现(Jdk动态代理)和继承模式(Cglib字节码)

1 基于实现接口的方式实现静态代理

public interface OrderService {

    String addOrder(String orderId, String orderName);
}
public class OrderServiceImpl implements OrderService {

    @Override
    public String addOrder(String orderId, String orderName) {
        System.out.println("OrderServiceImpl真实方法执行...");
        return orderId + "," + orderName;
    }
}
public class OrderServiceProxy implements OrderService {

    /**
     * 被代理类
     */
    private OrderServiceImpl orderServiceImpl;

    public OrderServiceProxy(OrderServiceImpl orderServiceImpl) {
        this.orderServiceImpl = orderServiceImpl;
    }

    @Override
    public String addOrder(String orderId, String orderName) {
        System.out.println("采用代理模式统一打印日志 orderId:" + orderId + ",orderName:" + orderName);
        return orderServiceImpl.addOrder(orderId, orderName);

    }
}
public class Test001 {
    public static void main(String[] args) {
        OrderService orderServiceProxy = new OrderServiceProxy(new OrderServiceImpl());
        orderServiceProxy.addOrder("mayikt", "111");
    }
}

运行结果:
请添加图片描述
2 基于继承的模式实现静态代理

public class OrderServiceProxy extends OrderServiceImpl {

    @Override
    public String addOrder(String orderId, String orderName) {
        System.out.println("采用代理模式统一打印日志 orderId:" + orderId + ",orderName:" + orderName);
        return super.addOrder(orderId, orderName);
    }
}
public class Test001 {
    public static void main(String[] args) {
        OrderService orderServiceProxy = new OrderServiceProxy();
        orderServiceProxy.addOrder("mayikt", "111");
    }
}

3 静态代理与动态代理模式的区别

静态代理模式缺陷:需要人工手写代理类
动态代理方式:采用程序动态生成代理类,不需要程序员人工手写

动态代理实现方式
jdk动态代理和cglib动态代理

4 基于JDK动态代理的使用

Jdk动态代理模式底层实现

public class JdkInvocationHandler implements InvocationHandler {

    /**
     * 目标对象(被代理类)
     */
    private Object target;

    public JdkInvocationHandler(Object object) {
        this.target = object;
    }

    /**
     * @param proxy  jdk动态生成的代理类
     * @param method 不是真正的目标方法,接口中的方法,底层通过反射执行真正目标方法
     * @param args   代理类参数
     *               使用生成的代理类去拦截回调
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        System.out.println("jdk动态代理执行开始>>>" + args[0]);
        // 执行目标方法 java反射执行目标方法
        Object result = method.invoke(target, args);
        System.out.println("jdk动态代理执行结束>>>" + args[0]);
        return result;
    }

    /**
     * 使用jdk创建代理类对象
     *
     * @param <T>
     * @return
     */
    public <T> T getProxy() {
        // ClassLoader 读取代理类class文件
        // Class<?>[] interfaces 基于该接口拼接代理类的源代码
        // InvocationHandler this
        return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }
}
public class Test002 {
    public static void main(String[] args) {
        JdkInvocationHandler jdkInvocationHandler = new JdkInvocationHandler(new OrderServiceImpl());
        // 创建代理类
        OrderService proxy = jdkInvocationHandler.getProxy();
        proxy.addOrder("mayikt", "maplefire");
    }
}

运行结果:
请添加图片描述

5 为什么Jdk动态代理不能用子类接收

获取代理的生成的class文件
System.getProperties().put(“sun.misc.ProxyGenerator.saveGeneratedFiles”, “true”);
Jdk动态生成的代理类:$Proxy0
请添加图片描述

注意:$Proxy0基于接口的方式实现,不能转换为子类(不能用子类接收)

6 JDK生成的代理类$Proxy源码分析

Java反射机制无法获取方法的参数名称,只可以获取方法的参数类型。
$Proxy0源码分析

public class $Proxy0 implements OrderService {

    /**
     * 回调类
     */
    private InvocationHandler h;

    public $Proxy0(InvocationHandler h) {
        this.h = h;
    }

    @Override
    public final String addOrder(String var1, String var2) {
        try {
            // m3 获取orderService接口的addOrder方法
            Method m3 = Class.forName("com.mayikt.service.OrderService").getMethod("addOrder", Class.forName("java.lang.String"), Class.forName("java.lang.String"));
            return (String) this.h.invoke(this, m3, new Object[]{var1, var2});
        } catch (RuntimeException | Error var4) {
            throw var4;
        } catch (Throwable var5) {
            throw new UndeclaredThrowableException(var5);
        }
    }
}
public class JdkInvocationHandler implements InvocationHandler {

    /**
     * 目标对象(被代理类)
     */
    private Object target;

    public JdkInvocationHandler(Object object) {
        this.target = object;
    }

    /**
     * @param proxy  jdk动态生成的代理类
     * @param method 不是真正的目标方法,接口中的方法,底层通过反射执行真正目标方法
     * @param args   代理类参数
     *               使用生成的代理类去拦截回调
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        System.out.println("jdk动态代理执行开始>>>" + args[0]);
        // 执行目标方法 java反射执行目标方法
        Object result = method.invoke(target, args);
        System.out.println("jdk动态代理执行结束>>>" + args[0]);
        return result;
    }

    public <T> T getProxy() {
        return (T) new $Proxy0(this);
    }
}

7 手写JDK动态代理

手写javaJDK动态代理实现思路

  1. 根据接口信息动态拼接代理类的源代码$Proxy.java
  2. $ Proxy.java编译为$Proxy.class
  3. 基于类加载器动态读取$Proxy.class到内存中
  4. 使用java的反射机制初始化 走有参构造函数

手写javaJDK动态代理实现

public class MyProxy {

    private static String rt = "\r\t";

    /**
     *
     * @param classLoader
     * @param classInfo
     * @param h
     * @return
     */
    public static Object newProxyInstance(JavaClassLoader classLoader,
                                          Class<?> classInfo,
                                          MayiktInvocationHandler h) {
        try {
            //  1.根据接口的信息拼接java源代码
            Method[] methods = classInfo.getMethods();
            String proxyClass = "package com.mayikt.service.proxy;" + rt
                    + "import java.lang.reflect.Method;" + rt
                    + "import com.mayikt.service.proxy.jdk.MayiktInvocationHandler;" + rt
                    + "import java.lang.reflect.UndeclaredThrowableException;" + rt
                    + "public class $Proxy0 implements " + classInfo.getName() + "{" + rt
                    + "MayiktInvocationHandler h;" + rt
                    + "public $Proxy0(MayiktInvocationHandler h)" + "{" + rt
                    + "this.h= h;" + rt + "}"
                    + getMethodString(methods, classInfo) + rt + "}";
            // 2.将java源代码写入到本次磁盘
            String filename = "d:/code/$Proxy0.java";
            File f = new File(filename);
            FileWriter fw = new FileWriter(f);
            fw.write(proxyClass);
            fw.flush();
            fw.close();
            // 3.需要将$Proxy.java编译成$Proxy.class
            JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
            StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
            Iterable units = fileMgr.getJavaFileObjects(filename);
            JavaCompiler.CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units);
            t.call();
            fileMgr.close();
            // 4.使用类加载器读取该class文件
            Class<?> proxy = classLoader.findClass("$Proxy0");
            Constructor<?> constructor = proxy.getConstructor(MayiktInvocationHandler.class);
            // 5.使用java的反射机制初始化 走有参构造函数
            Object o = constructor.newInstance(h);
            return o;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }

    }

    public static String getMethodString(Method[] methods, Class intf) {
        String proxyMe = "";

        for (Method method : methods) {
            Class<?>[] parameterTypes = method.getParameterTypes();
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < parameterTypes.length; i++) {
                sb.append(parameterTypes[i].getName() + " ver" + (i + 1));
                if (i < parameterTypes.length - 1) {
                    sb.append(" ,");
                }

            }
            String parameterStr = sb.toString();
            proxyMe = "public " + method.getReturnType().getName() + " " + method.getName() + " ( " + parameterStr + " ) { " +
                    "try {   Method m3 = Class.forName(\"com.mayikt.service.OrderService\").getMethod(\"addOrder\", Class.forName(\"java.lang.String\"), Class.forName(\"java.lang.String\"));" +
                    "return (String) h.invoke(this, m3, new Object[]{ver1, ver2}); } catch (RuntimeException | Error var4) {  throw var4;  } catch (Throwable var5) {   throw new UndeclaredThrowableException(var5); } " +
                    "" +
                    " } ";

        }
        return proxyMe;
    }

    public static void main(String[] args) {
        MyProxy.newProxyInstance(null, OrderService.class, null);
    }
}
public interface MayiktInvocationHandler {
    Object invoke(Object proxy, Method method, Object[] args) throws Throwable;

}
public class MayiktInvocationHandlerImpl implements MayiktInvocationHandler {

    private Object target;

    public MayiktInvocationHandlerImpl(Object object) {
        this.target = object;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("jdk动态代理执行开始>>>" + args[0]);
        // 执行目标方法 java反射执行目标方法
        Object result = method.invoke(target, args);
        System.out.println("jdk动态代理执行结束>>>" + args[0]);
        return result;
    }

    public <T> T getProxy() {
        return (T) MyProxy.newProxyInstance(new JavaClassLoader(), target.getClass().getInterfaces()[0], this);
    }
}
public class JavaClassLoader extends ClassLoader {

    private File classPathFile;

    public JavaClassLoader() {
//        String classPath=JavaClassLoader.class.getResource("").getPath();
        String classPath = "D:\\code";
        this.classPathFile = new File(classPath);
    }

    @Override
    public Class<?> findClass(String name) throws ClassNotFoundException {
        String className = JavaClassLoader.class.getPackage().getName() + "." + name;
        if (classPathFile != null) {
            File classFile = new File(classPathFile, name.replaceAll("\\.", "/") + ".class");
            if (classFile.exists()) {
                FileInputStream in = null;
                ByteArrayOutputStream out = null;
                try {
                    in = new FileInputStream(classFile);
                    out = new ByteArrayOutputStream();
                    byte[] buff = new byte[1024];
                    int len;
                    while ((len = in.read(buff)) != -1) {
                        out.write(buff, 0, len);
                    }
                    return defineClass(className, out.toByteArray(), 0, out.size());
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    if (in != null) {
                        try {
                            in.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                    if (out != null) {
                        try {
                            out.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
        return null;
    }
}
public class Test003 {
    public static void main(String[] args) {
        MayiktInvocationHandlerImpl mayiktInvocationHandler = new MayiktInvocationHandlerImpl(new OrderServiceImpl());
        OrderService proxy = mayiktInvocationHandler.getProxy();
        String s = proxy.addOrder("mayikt", "maplefire");
    }
}

运行结果:
在这里插入图片描述
生成的$Proxy0.java文件
在这里插入图片描述
源码下载地址(mayikt_designPattern_1.rar):
链接:https://pan.baidu.com/s/1wWKZN1MbXICZVW1Vxtwe6A
提取码:fire

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值