动态代理学习记录

2022-08-23 11:39:15

设计模式学习记录

一:代理模式

  1. JDK动态代理,代码实现案例
    public interface MethodInterface {
    
        /**
         * 实现的方法
         * @param age 年龄
         * @param name 姓名
         */
        void implementsMethod(int age,String name);
    }
    public class OrderService implements MethodInterface {
        @Override
        public void implementsMethod(int age,String name) {
            System.out.println("这是Order 服务执行的内容" + "name:" + name + ",age:" + age);
        }
    }
    public class ShopService implements MethodInterface {
        @Override
        public void implementsMethod(int age,String name) {
            System.out.println("这是shop 服务执行的内容" + "name:"+name +",age:"+age);
        }
    }
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    /**
     * @desc 代理类需要实现InvocationHandler 接口
     * @date 2020/10/6 0:39
     */
    public class ProxyInvokerEnhance implements InvocationHandler {
    
        private Object target;
    
        public ProxyInvokerEnhance(Object target) {
            this.target = target;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            beforeDeal();
            Object invoke = method.invoke(this.target, args);
            afterDeal();
            return invoke;
        }
    
        private void afterDeal() {
            System.out.println("在代理之前处理一下数据,记录信息");
        }
    
        private void beforeDeal() {
            System.out.println("在代理之后处理一下数据,收尾工作");
        }
    	// 通过反射获取新生成类的对象,新生成的类实现了原有类的所有接口
        public Object getInstance(Object obj) {
            this.target = obj;
            Class<?> clazz = target.getClass();
            return Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);
        }
    
    
    }
    public class ProxyTest {
        public static void main(String[] args) {
            OrderService orderService = new OrderService();
            ProxyInvokerEnhance invokerEnhance = new ProxyInvokerEnhance(orderService);
            //保存JDK动态生成的代码位置
    //        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
            /* 此处只能是接口强转,由于新生成的对象实现了原有类的接口,但不能直接转为接口的实现类*/
            MethodInterface proxyOrderService = (MethodInterface) invokerEnhance.getInstance(orderService);
            proxyOrderService.implementsMethod(5,"11");
        }
    }
    // 这个类是代理过程中生成的类,无关运行,由ProxyTest 测试类中保存下来
    // Source code recreated from a .class file by IntelliJ IDEA
    // (powered by Fernflower decompiler)
    //
    
    package com.sun.proxy;
    
    import com.example.designPattern.proxy.MethodInterface;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.lang.reflect.UndeclaredThrowableException;
    
    public final class $Proxy0 extends Proxy implements MethodInterface {
        private static Method m1;
        private static Method m2;
        private static Method m3;
        private static Method m0;
    
        public $Proxy0(InvocationHandler var1) throws  {
            super(var1);
        }
    
        public final boolean equals(Object var1) throws  {
            try {
                return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
            } catch (RuntimeException | Error var3) {
                throw var3;
            } catch (Throwable var4) {
                throw new UndeclaredThrowableException(var4);
            }
        }
    
        public final String toString() throws  {
            try {
                return (String)super.h.invoke(this, m2, (Object[])null);
            } catch (RuntimeException | Error var2) {
                throw var2;
            } catch (Throwable var3) {
                throw new UndeclaredThrowableException(var3);
            }
        }
    
        public final void implementsMethod(int var1, String var2) throws  {
            try {
                super.h.invoke(this, m3, new Object[]{var1, var2});
            } catch (RuntimeException | Error var4) {
                throw var4;
            } catch (Throwable var5) {
                throw new UndeclaredThrowableException(var5);
            }
        }
    
        public final int hashCode() throws  {
            try {
                return (Integer)super.h.invoke(this, m0, (Object[])null);
            } catch (RuntimeException | Error var2) {
                throw var2;
            } catch (Throwable var3) {
                throw new UndeclaredThrowableException(var3);
            }
        }
    
        static {
            try {
                m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
                m2 = Class.forName("java.lang.Object").getMethod("toString");
                m3 = Class.forName("com.example.designPattern.proxy.MethodInterface").getMethod("implementsMethod", Integer.TYPE, Class.forName("java.lang.String"));
                m0 = Class.forName("java.lang.Object").getMethod("hashCode");
            } catch (NoSuchMethodException var2) {
                throw new NoSuchMethodError(var2.getMessage());
            } catch (ClassNotFoundException var3) {
                throw new NoClassDefFoundError(var3.getMessage());
            }
        }
    }

    案例中共5个类:

    MethodInterface
    OrderService
    ProxyInvokerEnhance
    ProxyTest
    ShopService

    代理的主要作用:对原有的类功能进行增强,注重对过程的干预,在原本处理的逻辑前后,进行额外的操作。

    要求:被代理的对象需要实现接口,代理的过程中需要用到实现的接口

    代理实现过程:

    1)拿到被代理被的引用,并且通过反射获取它的所有接口

    2)JDK Proxy类重新生成一个新的类,实现了被代理对象的所有接口方法

    1. 动态生成Java 代码,把增强的逻辑写到新生成的代码中

    4)编译生成新的class文件

    5)加载并运行新的class文件

类图

关于JDK动态代理常见的问题:(

问题参考地址

  • 1.动态代理解决了什么问题?

    答:首先它是一个代理机制,代理可以看作是对调用目标的一个包装,这样我们对目标代码的调用不是直接发生的,而是通过代理完成,通过代理可以让调用者与实现者之间解耦。比如进行 RPC 调用,通过代理,可以提供更加友善的界面;还可以通过代理,做一个全局的拦截器。

    2.动态代理和反射的关系是什么?

    答:反射可以用来实现动态代理,但动态代理还有其他的实现方式,比如 ASM(一个短小精悍的字节码操作框架)、cglib 等。

    3.以下描述错误的是?

    A:cglib 的性能更高

    B:Spring 中有使用 cglib 来实现动态代理

    C:Spring 中有使用 JDK 原生的动态代理

    D:JDK 原生动态代理性能更高

    答:D

    题目解析:Spring 动态代理的实现方式有两种:cglib 和 JDK 原生动态代理。

    4.请补全以下代码?

    class MyReflect {
     // 私有方法
     private void privateMd() {
     System.out.println("Private Method");
     }
    }
    class ReflectTest {
     public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException {
     Class myClass = Class.forName("MyReflect");
     Object object = myClass.newInstance();
     // 补充此行代码
     method.setAccessible(true);
     method.invoke(object);
     }
    }

    答:Method method = myClass.getDeclaredMethod(“privateMd”);

    题目解析:此题主要考的是私有方法的获取,私有方法的获取并不是通过 getMethod() 方式,而是通过 getDeclaredMethod() 获取的。

    5.cglib 可以代理任何类这句话对吗?为什么?

    答:不完全对,因为 cglib 只能代理可以有子类的普通类,对于像最终类(final),cglib 是不能实现动态代理的,因为 cglib 的底层是通过继承代理类的子类来实现动态代理的,所以不能被继承类无法使用 cglib。

    6.JDK 原生动态代理和 cglib 有什么区别?

    答:JDK 原生动态代理和 cglib 区别如下:

    JDK 原生动态代理是基于接口实现的,不需要添加任何依赖,可以平滑的支持 JDK 版本的升级;

    cglib 不需要实现接口,可以直接代理普通类,需要添加依赖包,性能更高。

    7.为什么 JDK 原生的动态代理必须要通过接口来完成?

    答:这是由于 JDK 原生设计的原因,原本的设计中需要通过接口获取,动态代理的实现方法 newProxyInstance() 的源码如下:

    /**
     * ......
     * @param loader the class loader to define the proxy class
     * @param interfaces the list of interfaces for the proxy class to implement
     * ......
     */ 
    @CallerSensitive
    public static Object newProxyInstance(ClassLoader loader,
     Class<?>[] interfaces,
     InvocationHandler h)
     throws IllegalArgumentException
    {
  • 8.JDK 动态代理中,目标对象调用自己的另一个方法,会经过代理对象么

  • :内部调用方法使用的对象是目标对象本身,被调用的方法不会经过代理对象。

2 cglib代理

需要引入依赖:

<!--cglib 代理需要的jar依赖,版本自选-->
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>2.2.2</version>
        </dependency>

代码如下:

public class UserService {

    public void getUserName(String name){
        System.out.println("cglib 模拟获取用户姓名");
    }
}
package com.example.designPattern.proxy.cglibproxy;

import com.example.designPattern.proxy.jdkproxy.MethodInterface;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * cglib代理依赖于继承关系,通过动态生成被代理的对象的子类实现
 * 要求:被代理的对象不能被 final 修饰
 * @auther djy
 * @date 2020/10/6 3:09
 */
public class CglibProxyMethodIntercepte implements MethodInterceptor {

    /**
     * 设置代理对象,与jdk动态代理不同,此处传入的是class 类
     * @param clazz 被代理的字节码对象
     * @return 增强后的对象
     * @throws Exception
     */
    public Object getInstance(Class<?> clazz) throws Exception{
        Enhancer enhancer = new Enhancer();
        // 设置被代理对象的父类
        enhancer.setSuperclass(clazz);
        // 设置回调
        enhancer.setCallback(this);
        // 创建新的代理对象,通过继承
        return enhancer.create();
    }
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        beforeDeal();
        Object o1 = methodProxy.invokeSuper(o, objects);
        afterDeal();
        return o1;
    }

    private void afterDeal() {
        System.out.println("在代理之前处理一下数据,记录信息");
    }

    private void beforeDeal() {
        System.out.println("在代理之后处理一下数据,收尾工作");
    }
}
public class CglibTest {
    public static void main(String[] args) {
        try {
            UserService userService = (UserService)new CglibProxyMethodIntercepte().getInstance(UserService.class);
            userService.getUserName("hehe");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

JDK 动态代理与cglib 代理主要区别:

jdk 被代理的类必须实现接口,cglib代理的类,不能为最终类,需要可以被继承

两者效率比较:在1.6和1.7的时候,JDK动态代理的速度要比CGLib动态代理的速度要慢,但是并没有10倍差距,在JDK1.8的时候,JDK动态代理的速度已经比CGLib动态代理的速度快很多了(此处并没有进行实际验证,参考别人验证的数据)

  • 作者:djyzq1450
  • 原文链接:https://blog.csdn.net/qq_39797304/article/details/108953806
    更新时间:2022-08-23 11:39:15