且来玩一玩动态代理(极深度版本)

2023-05-16

恶心的东西就更要花时间让它变得更恶心🧎‍♀️ ----xmonster

文章目录

  • 开篇
  • 走一遍百度百科
    • 动态代理类
    • 代理机制及特点(使用步骤)
  • 动态代理的工具类代码
  • 分析
    • 实现方式
      • Proxy.getProxyClass
      • $Proxy0
      • getConstructor
      • newInstance

开篇

可以去看《设计模式之禅》(第2版)里对代理模式的理解,他利用的是“游戏打怪”的例子,我觉得非常的生动形象,理解起来非常通透!如果你对代理模式不是很通透,那么动态代理你就自然比较难理解了,不做无用功,先去补基础 (别忘了补完回来);如果你已经特别了解,那么就继续往下吧!
来一个图吧,没有任何意义,只是为了提个醒,这是上面那本书的流程图
本篇博文较长,望耐心看完🐈
💜💜
在这里插入图片描述
🧡🧡🧡🧡🧡
在这里插入图片描述
💛💛💛💛💛

走一遍百度百科

动态代理类

Java动态代理类位于Java.lang.reflect包下,这里和Java的反射是息息相关的,如果不了解Java的反射,是比较难理解动态代理的,这里有一篇我对Java反射的基本理解总结,主要参考百度百科:大话Java反射机制

一般主要涉及到以下两个类:

一、Interface InvocationHandler:该接口中仅定义了一个方法Object:invoke(Object obj,Method method,Object[] args)。在实际使用时,第一个参数obj一般是指代理类,method是被代理的方法,args为该方法的参数数组。这个抽象方法在代理类中动态实现。

二、Proxy:该类即为动态代理类其中主要包含以下内容:
Protected Proxy(InvocationHandler h):构造函数,估计用于给内部的h赋值。
Static Class getProxyClass (ClassLoader loader,Class[] interfaces):获得一个代理类,其中loader是类装载器,interfaces是真实类所拥有的全部接口的数组。(为某个接口创建代理)
重点看以下方法:

Static Object newProxyInstance(ClassLoader loader,Class[] interfaces,InvocationHandler h):返回代理类的一个实例,返回后的代理类可以当作被代理类使用(可使用被代理类的在Subject接口中声明过的方法)。
所谓Dynamic Proxy是这样一种class:它是在运行时生成的class,在生成它时你必须提供一组interface给它然后该class就宣称它实现了这些 interface。你当然可以把该class的实例当作这些interface中的任何一个来用。当然啦,这个Dynamic Proxy其实就是一个Proxy,它不会替你作实质性的工作,在生成它的实例时你必须提供一个handler,由它接管实际的工作。

代理机制及特点(使用步骤)

通过实现 InvocationHandler 接口创建自己的调用处理器;
通过为 Proxy 类指定 ClassLoader 对象一组 interface来创建动态代理类;
通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型
通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。

上面没看懂,没关系,下面慢慢来🥂
以下我们的代码就按照这个顺序来完成对应的动态代理实现

动态代理的工具类代码

下面的代码注解是我个人的一些理解,有点乱,但很nice,也可直接跳过~

package com.xmonster.demo01;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

//等会用这个类自动生成代理类!
public class ProxyInvocationHandler implements InvocationHandler {

//    Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),
//            new Class<?>[] { Foo.class },
//            handler);

//    被代理的接口
//    1、代理一个接口  这个接口说白了是谁,就是真实对象;真实对象会实现那个接口,我们代理它就完了
    private Object target;
    public void setTarget(Object target) {
        this.target = target;
    }

    //生成得到代理类
    public Object getProxy(){
// getClassLoader()类加载器:它负责将字节码文件加载到内存,创建Class对象
//        target.getClass().getClassLoader()   通过target示例得到的Loader()信息
//         target.getClass().getInterfaces()   得到对应的interfaces信息(反射)
//      System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
//      target.getClass().getClassLoader()
        /*    target.getClass()得到它的class对象
         *    getClassLoader():每个Class对象都会有一个方法,可以获取到它的ClassLoader
         * target.getClass().getClassLoader():获取类以及获取到这个类的类加载器
         *
         *
         *
         * */


      return  Proxy.newProxyInstance(target.getClass().getClassLoader(),
              target.getClass().getInterfaces(),
              this);
//   newProxyInstance:返回指定接口的代理类的实例,该接口将方法调用分派给指定的调用处理程序。
// loader - 类加载器来定义代理类
//interfaces - 代理类实现的接口列表
//h - 调度方法调用的调用处理函数

    }
    //处理代理实例,并返回结果
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //代理的是一个接口
        //动态代理的本质,就是使用反射机制来实现
        setHost();
        Object invoke = method.invoke(target, args);
        return invoke;
    }
}

这段代码相信有经验的老狗们都不陌生,这段代码是我看狂神的视频写下的,所以用的也是他的例子,房东和中介的例子,那么现在让我们一探究竟上述代码含义和使用
先来一段测试类吧

package com.xmonster.demo01;

public class Client {
    public static void main(String[] args) {
//        真实对象
        UserService userService = new UserServiceImpl();
//        代理对象
        ProxyInvocationHandler pih = new ProxyInvocationHandler();
//        生成代理角色
        pih.setTarget(userService);   //设置要代理的对象
//        动态生成、获得代理类,获得代理类对象
//      注意这里的强制转换
        UserService proxy =(UserService)  pih.getProxy();
//        Object proxy = pih.getProxy();
//        调用响应的代理的方法即可
        proxy.query();
    }
}

分析

回到上面我们讲的,代理类和被代理类都是实现了一个“接口”,只不过代理类传递了一个参数(传递被代理类),然后帮助被代理类干事情,所以重点就是“接口”
(代理和实际对象一般都是有相同的接口)

这也就是为什么

UserService proxy =(UserService)  pih.getProxy();

这里要强转的原因

这里不像适配器和装饰器,因为代理一般不改变接口

实现方式

实现方式一般有2种,一种是Java SDK,一种是cglib
这里关于newProxyInstance、InvocationHandler 的例子和参数理解在这篇博客:
且玩一玩动态代理之小栗子
因为这里也是需要结合这个例子来玩,所以先看上面的例子,对照理解
将SimpleInvocationHandler 的主函数改成另一种方式实现:

Class<?> proxyClass = Proxy.getProxyClass(IService.class.getClassLoader(),
              new Class<?>[] {IService.class});

      Constructor<?> ctor = proxyClass.getConstructor(new Class<?>[] {InvocationHandler.class});
      InvocationHandler handle = new SimpleInvocationHandler(realService);

      IService proxyService = (IService) ctor.newInstance(handle);
      proxyService.sayHello();

理解步骤:

  1. 通过Proxy.getProxyClass创建了代理类的“定义”,类定义会被缓存;
  2. 获取了类的构造方法,其中构造方法有一个InvocationHandler类型的参数
  3. 创建InvocationHandler对象,创建了代理对象

Proxy.getProxyClass

这个方法的源码:

    public static Class<?> getProxyClass(ClassLoader loader,
                                         Class<?>... interfaces)
        throws IllegalArgumentException
    {
        Class<?> caller = System.getSecurityManager() == null
                              ? null
                              : Reflection.getCallerClass();

        return getProxyConstructor(caller, loader, interfaces)
            .getDeclaringClass();
    }

可以看到这个方法需要两个参数,一个是ClassLoader,另一个是接口数组

给定类加载器和接口数组的代理类的java.lang.Class对象。 代理类将由指定的类加载器定义,并将实现所有提供的接口。 如果任何给定的接口是非公开的,则代理类将是非公开的。 如果类加载器已经定义了接口相同置换的代理类,那么将返回现有的代理类; 否则,这些接口的代理类将被动态生成并由类加载器定义。
这个方法会动态生成一个类,类名以$Proxy开头,然后后面会跟一些数字之类的

🔺参数
loader - 类加载器来定义代理类
interfaces - 要实现的代理类的接口列表
🔺结果
在指定的类加载器中定义并实现指定接口的代理类

该方法给定类加载器接口数组的代理类的java.lang.Class对象。 代理类将由指定的类加载器定义,并将实现所有提供的接口。 如果任何给定的接口是非公开的,则代理类将是非公开的。 如果类加载器已经定义了接口相同置换的代理类,那么将返回现有的代理类; 否则,这些接口的代理类将被动态生成并由类加载器定义。

$Proxy0

这里分析$Proxy0的源码的话就会特别清楚(代码比较长,这里没有放出来了,大家可以百度,样子都长得差不多,主要看它的构造方法和结构)

$Proxy0的父类是Proxy,并且它有一个构造方法,接受一个InvocationHandler类型的参数,保存为了实例变量h,h定义在父类的Proxy种,它实现了接口IService,对于每个方法,都会调用InvocationHandler的invoke方法,对于Object中的许多方法,也同样转给了InvocationHandler
这个类定义本身与被代理的对象没有关系,与InvocationHandler的具体实现也没有关系,而主要和接口数组有关,给定这个接口数组,它就会动态创建每个接口的实现代码,实现就是转发给InvocationHandler,与被代理对象以及它的调用由InvocationHandler的实现管理

接下来就是获取构造方法,创建代理对象!

getConstructor

@CallerSensitive
public Constructor<T> getConstructor(Class<?>... parameterTypes)
    throws NoSuchMethodException, SecurityException
{
    SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
        checkMemberAccess(sm, Member.PUBLIC, Reflection.getCallerClass(), true);
    }
    return getReflectionFactory().copyConstructor(
        getConstructor0(parameterTypes, Member.PUBLIC));
}
  • getConstructor方法返回一个Constructor对象,该对象反映Constructor对象表示的类的指定的公共类函数。 parameterTypes参数是以声明顺序标识构造函数的形式参数类型的类对象的数组。 如果此类对象表示在非静态上下文中声明的内部类,则形式参数类型将显式包围实例作为第一个参数。

newInstance

public T newInstance(Object ... initargs)
        throws InstantiationException, IllegalAccessException,
               IllegalArgumentException, InvocationTargetException
  • 使用由此Constructor对象表示的构造函数,使用指定的初始化参数创建和初始化构造函数的声明类的新实例。 个别参数自动解包以匹配原始形式参数,原始参考参数和参考参数都需要进行方法调用转换。
  • 如果构造函数正常完成,则返回新创建和初始化的实例。
  • 参数initargs - 要作为构造函数调用的参数传递的对象的数组; 原始类型的值被包装在适当类型的包装器对象中(例如float中的float )

结果
通过调用此对象代表的构造函数创建的新对象

这样整个过程基本结束

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

且来玩一玩动态代理(极深度版本) 的相关文章

随机推荐