动态代理学习记录

    科技2024-08-21  24

    设计模式学习记录

    一:代理模式

    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类重新生成一个新的类,实现了被代理对象的所有接口方法

    动态生成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动态代理的速度快很多了(此处并没有进行实际验证,参考别人验证的数据)

    Processed: 0.013, SQL: 8