ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

Day6-Java 动态代理

2022-01-01 09:04:55  阅读:131  来源: 互联网

标签:java Day6 Object 代理 实例 Java 动态 Class


JAVA动态代理

这个主要实现的其实也就是可以不修改原来的代码来实现方法的增强,具体实现看以下代码。

0x01 API

主要方法

package java.lang.reflect;

import java.lang.reflect.InvocationHandler;

/**
 * Creator: yz
 * Date: 2020/1/15
 */
public class Proxy implements java.io.Serializable {

  // 省去成员变量和部分类方法...

    /**
     * 获取动态代理处理类对象
     *
     * @param proxy 返回调用处理程序的代理实例
     * @return 代理实例的调用处理程序
     * @throws IllegalArgumentException 如果参数不是一个代理实例
     */
    public static InvocationHandler getInvocationHandler(Object proxy)
            throws IllegalArgumentException {
        ...
    }

    /**
     * 创建动态代理类实例
     *
     * @param loader     指定动态代理类的类加载器
     * @param interfaces 指定动态代理类的类需要实现的接口数组
     * @param h          动态代理处理类
     * @return 返回动态代理生成的代理类实例
     * @throws IllegalArgumentException 不正确的参数异常
     */
    public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
            throws IllegalArgumentException {
        ...
    }

    /**
     * 创建动态代理类
     *
     * @param loader     定义代理类的类加载器
     * @param interfaces 代理类要实现的接口列表
     * @return 用指定的类加载器定义的代理类,它可以实现指定的接口
     */
    public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces) {
        ...
    }

    /**
     * 检测某个类是否是动态代理类
     *
     * @param cl 要测试的类
     * @return 如该类为代理类,则为 true,否则为 false
     */
    public static boolean isProxyClass(Class<?> cl) {
        return java.lang.reflect.Proxy.class.isAssignableFrom(cl) && proxyClassCache.containsValue(cl);
    }

    /**
     * 向指定的类加载器中定义一个类对象
     *
     * @param loader 类加载器
     * @param name   类名
     * @param b      类字节码
     * @param off    截取开始位置
     * @param len    截取长度
     * @return JVM创建的类Class对象
     */
    private static native Class defineClass0(ClassLoader loader, String name, byte[] b, int off, int len);

}

newProxyInstance:获取一个新生成的动态代理类实例

InvocationHandler:获取类

getProxyClass:创建class

还有invocationHandler的方法

 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;

0x02 使用java.lang.reflect.Proxy动态创建类对象

其实就是他也有native的方法叫defineClass0,可以直接动态创建类。(直接和jvm说hello word)

大概流程就是

  1. 准备一个ClassLoader可以用系统的那个
  2. 获取java.lang.reflect.ProxydefineClass0(ClassLoader loader, String name,byte[] b, int off, int len)方法。
  3. 返shemethodinvoke类就出出来了。

0x03 使用JDK动态代理生成FileSystem动态代理类实例

 *     Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),
 *                                          new Class() { Foo.class },
 *                                          handler);

handler 需要实现InvocationHandler接口

要覆盖他的构造方法和invoke方法

// 创建UnixFileSystem类实例
FileSystem fileSystem = new UnixFileSystem();

// 创建动态代理处理类
InvocationHandler handler = new JDKInvocationHandler(fileSystem);

// 通过指定类加载器、类实现的接口数组生成一个动态代理类
Class proxyClass = Proxy.getProxyClass(
      FileSystem.class.getClassLoader(),// 指定动态代理类的类加载器
      new Class[]{FileSystem.class}// 定义动态代理生成的类实现的接口
);

// 使用反射获取Proxy类构造器并创建动态代理类实例
FileSystem proxyInstance = (FileSystem) proxyClass.getConstructor(
      new Class[]{InvocationHandler.class}).newInstance(new Object[]{handler}
);
//Proxy(InvocationHandler h(接口)) 

Class类是个很有用的类,用它可以实现反射机制。理解 new Class[]{FileSystem.class} 这是一个接口的写法。Class类的对象用于表示当前运行的 Java 应用程序中的类和接口。

package com.anbai.sec.proxy;
import java.io.Serializable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class JDKInvocationHandler implements InvocationHandler, Serializable {
    private final Object target;
    public JDKInvocationHandler(Object target) {
        this.target = target;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 为了不影响测试Demo的输出结果,这里忽略掉toString方法
        if ("toString".equals(method.getName())) {
            return method.invoke(target, args);
        }
        System.out.println("即将调用[" + target.getClass().getName() + "]类的[" + method.getName() + "]方法...");
        Object obj = method.invoke(target, args);
        System.out.println("已完成[" + target.getClass().getName() + "]类的[" + method.getName() + "]方法调用...");
        return obj;
    }
}

这个其实之前在javaweb中学过,就是加一个中间件,你调用proxy的这个方法,它过滤以下,加点东西进去,再选择是不是要触发。

image-20220101012704699

必须时接口,这样就可以反射调用。

贴一下大哥的话

动态代理生成出来的类有如下技术细节和特性:

  1. 动态代理的必须是接口类,通过动态生成一个接口实现类来代理接口的方法调用(反射机制)。

    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
    

    必须时接口 new Class[]{FileSystem.class}

  2. 动态代理类会由java.lang.reflect.Proxy.ProxyClassFactory创建。

    proxyClassCache.get(loader, interfaces);
    
    // If the proxy class defined by the given loader implementing
    // the given interfaces exists, this will simply return the cached copy;
    // otherwise, it will create the proxy class via the ProxyClassFactory
    
  3. ProxyClassFactory会调用sun.misc.ProxyGenerator类生成该类的字节码,并调用java.lang.reflect.Proxy.defineClass0()方法将该类注册到JVM

      */
                byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                    proxyName, interfaces, accessFlags);
                try {
                    return defineClass0(loader, proxyName,
                                        proxyClassFile, 0, proxyClassFile.length);
    
  4. 该类继承于java.lang.reflect.Proxy并实现了需要被代理的接口类,因为java.lang.reflect.Proxy类实现了java.io.Serializable接口,所以被代理的类支持序列化/反序列化

  5. 该类实现了代理接口类(示例中的接口类是com.anbai.sec.proxy.FileSystem),会通过ProxyGenerator动态生成接口类(FileSystem)的所有方法,

  6. 该类因为实现了代理的接口类,所以当前类是代理的接口类的实例(proxyInstance instanceof FileSystemtrue),但不是代理接口类的实现类的实例(proxyInstance instanceof UnixFileSystemfalse)。

  7. 该类方法中包含了被代理的接口类的所有方法,通过调用动态代理处理类(InvocationHandler)的invoke方法获取方法执行结果。

  8. 该类代理的方式重写了java.lang.Object类的toStringhashCodeequals方法。

  9. 如果过动态代理生成了多个动态代理类,新生成的类名中的0会自增,如com.sun.proxy.$Proxy0/$Proxy1/$Proxy2

0x04 动态代理类实例序列化问题

通过大哥的代码,我们知道,反序列化出来的上面反编译的proxy类,但是实例化的时响应的子类

参考:https://docs.oracle.com/javase/8/docs/technotes/guides/reflection/proxy.html#serial

也就是说动态代理生成的类在序列化的时候不会序列化它的成员变量。

标签:java,Day6,Object,代理,实例,Java,动态,Class
来源: https://blog.csdn.net/anwen12/article/details/122265119

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有