Java反射、类加载机制、动态代理

85 阅读6分钟

一、Class对象

  • Class对象可用来获取当前类的信息
    • 类信息被存在元空间(系统内存)
  • 获取Class对象的三种方式
    • Class.forName()获取(触发类加载机制)
    • 类名.class
    • Object.getClass()
    • getFields():获取能公开访问的字段
    • getDeclarField:获取实际类的字段(包括私有字段)

二、反射获取泛型类型

  • 调用Field#getGenericType()并将结果强制转换为ParameteredType

三、JDK动态代理

实现

  • JDK动态代理只能是接口实现类才可以使用
  • 主要类
    • java.lang.reflect.Proxy
    • java.lang.reflect.InvocationHandler
public class MainTest {

    public static void main(String[] args) {

        // 创建一个实例对象
        InterTest interTest = () -> {
            System.out.println("say: Hello");
            return "success";
        };

        // 通过Proxy方法:指定ClassLoader来创建指定接口的一个代理对象
        InterTest o = (InterTest) Proxy.newProxyInstance(MainTest.class.getClassLoader(), new Class[]{InterTest.class}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.err.println("before");
                method.invoke(interTest);
                return "null";
            }
        });

        System.out.println("结果:" + o.say());
    }
}

interface InterTest {
    String say();
}

动态生成代理类

  • 怎么调用的?
    • 通过继承Proxy类,并调用其中的InvocationHandler对象的invoke()方法进行执行
  • 为什么要继承Proxy类?
    • 就只是为了调用Proxy中保存的InvocationHandler对象
    • 但是我认为也可以将InvocationHandler放到动态代理类中,这样就不用再继承Proxy类,就可以实现代理类
  • 怎么保存生成的动态代理类
    • 在main方法中加入System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");
import java.lang.invoke.MethodHandles;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import org.example.proxy.ProxyInterface;

// 实现我们自定义需要代理的接口:ProxyInterface(动态代理的接口)
// 继承java.lang.reflect.Proxy类,这也是为什么只能代理接口
public final class $Proxy0 extends Proxy implements ProxyInterface {
    private static final Method m0;
    private static final Method m1;
    private static final Method m2;
    private static final Method m3;

    static {
        // 静态初始化中,找到所有Method对象
        try {
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("org.example.proxy.ProxyInterface").getMethod("method1");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }

    public $Proxy0(InvocationHandler var1) {
        super(var1);
    }

    public final void method1() {
        try {
            // 调用java.lang.reflect.Proxy中的InvocationHandler h的invoke传入Method对象和参数进行实际调用
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final boolean equals(Object var1) {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final String toString() {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    private static MethodHandles.Lookup proxyClassLookup(MethodHandles.Lookup var0) throws IllegalAccessException {
        if (var0.lookupClass() == Proxy.class && var0.hasFullPrivilegeAccess()) {
            return MethodHandles.lookup();
        } else {
            throw new IllegalAccessException(var0.toString());
        }
    }
}

四、反射Modifier类

  • 用于解码类和成员变量访问修饰符和final修饰符,可以用于判断是否被static等修饰符修饰。

image.png

五、Java类加载机制

类加载流程

加载

  1. 通过一个类的全限定名获取Class文件的二进制字节流
  2. 将Class文件描述的静态存储结构转化为方法区的运行时数据结构
  3. 生成java.lang.Class对象,作为方法区中这个类信息发访问入口

连接

  • 分为以下三步骤:验证、准备、解析

验证(-noverify 可以关闭大部分验证来提高类加载效率)

  • 文件格式验证:检查Class文件格式是否正确,需要读取Class的二进制字节流(连接和加载交叉进行部分)
  • 元数据验证:对元数据语义检验,保证没有与定义相悖。
    • 例如:final类不能被继承等
  • 字节码验证:检查Code属性部分,确保代码中没有对虚拟机产生危害的代码
    • JDK6后与Javac进行了联合优化:由javac验证生成StackMapTable,而虚拟机验证时只需检查StackMapTable。
  • 符号引用验证:检查类中依赖的外部类、方法、属性等的权限等是合法的

准备

  • 为static变量分配空间并设置初始化阶段的初始零值(如果有ConstantValue属性,则设置对应的值)
// 这个变量并没有ConstantValue属性,所以在准备阶段会设置为初始零值
public static int value = 123;

// final修饰出现了ConstantValue属性,所以在准备阶段就设置为123
public static final int value = 123;

解析

  • 将符号引用替换成直接引用(对象引用)

初始化

  1. 定义:根据程序主观代码去进行初始化
  2. 直接表示:调用进行初始化
  3. 类中没有static{} 时不会生成,虚拟机会保证执行子类的前执行了父类的
/*
这种只能赋值,但是不能被引用
因为准备阶段它有初始零值,所以可以赋值
但是从代码角度,该值并没有完成初始化,所以不能被引用
*/
public class RefTest {
	static {
		i = 10;
        // 编译时会出现非法前向引用问题
		System.out.println(i);
	}
	
	public static int i = 99;
}

使用

卸载

类加载器

  • Bootstrap ClassLoader:根类加载器(由C++实现)
    • JDK9之后由JVM和Java协作创造,获取该对象将返回NULL
  • Extension ClassLoader:扩展类加载器
    • JDK9之后替换为Platform ClassLoader
  • Application ClassLoader:应用类加载器(AppClassLoader)
  • User ClassLoader:自己实现的类加载器

双亲委派机制

  • 双亲委派机制的流程
    • 当一个类加载器收到类加载的请求时,会向上委派父类加载器进行加载,一直委派到Bootstrap ClassLoader。到达启动类加载器之后,在依次向下进行类加载请求
  • 意义
    • 能够保证类不被重复加载
    • 保证一些类安全性,如果由恶意的类替换发生,那么从BootStrapClassLoader加载 可以保证类的安全性

image.png

protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
{
    synchronized (getClassLoadingLock(name)) {
        // 检查是否已经加载,返回已加载的类的Class对象
        Class<?> c = findLoadedClass(name);
        if (c == null) {
            long t0 = System.nanoTime();
            try {
                // 双亲委派机制
                if (parent != null) {
                    // 调用上级的loadClass()方法再执行
                    c = parent.loadClass(name, false);
                } else {
                    // 从根加载器进行类加载
                    c = findBootstrapClassOrNull(name);
                }
            } catch (ClassNotFoundException e) {
                // ClassNotFoundException thrown if class not found
                // from the non-null parent class loader
            }

            if (c == null) {
                // 通过双亲委派机制没能加载类,使用findClass()进行类加载
                long t1 = System.nanoTime();
                c = findClass(name);

                // this is the defining class loader; record the stats
                PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                PerfCounter.getFindClasses().increment();
            }
        }
        if (resolve) {
            // 连接类,类加载流程中的连接动作(默认都不执行,交给JVM处理连接)
            resolveClass(c);
        }
        return c;
    }
}

自定义类加载器

image.png

类加载时机

  • 访问静态方法、变量(非宏定义)
  • 创建Class对象
  • 静态变量赋值

六、函数式编程的序列化 SerializedLambda

  • Java为了使用于函数式编程的Lambda也可以序列化,特地使用SerializedLambda进行序列化

private static SerializedLambda getSerializedLambda(SFunction<?, ?> sFunction) {
    Method method;
    try {
        method = sFunction.getClass().getDeclaredMethod("writeReplace");
        method.setAccessible(true);
        return (SerializedLambda) method.invoke(sFunction);
    } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
        throw new RuntimeException(e);
    }
}

private static String getFiledNameBySerializedLambda(SerializedLambda lambda) {
    String implMethodName = lambda.getImplMethodName();
    if (implMethodName.startsWith(GET_PREFIX)) {
        return Introspector.decapitalize(implMethodName.substring(GET_PREFIX.length()));
    } else if (implMethodName.startsWith(IS_PREFIX)) {
        return Introspector.decapitalize(implMethodName.substring(IS_PREFIX.length()));
    } else {
        throw new RuntimeException("无法解析字段名获取失败");
    }
}
@FunctionalInterface
public interface SFunction<T, R> extends Serializable {

    /**
     * get
     * @param obj 对象
     * @return 返回值
     */
    R get(T obj);
}

七、基本类型和包装类的Class对象

  1. 包装类和基本类,我们知道本质上并不是同一个类型。只是有了自动拆装箱才可以自动相互转换
  2. 所以包装类和基本类型的Class是不同的,这就可以区分了 | 包装类Class对象 | 对应的基本类型Class对象(通过包装类获取) | 对应的基本类型Class对象(直接获取) | | --- | --- | --- | | Integer.class | Integer.TYPE | int.class | | Xxx.class | Xxx.TYPE | xxx.class |

八、直接获取和Declared的区别

  • 反射直接获取:只能获取public权限的属性/方法,但是可以获取父类中的属性/方法
    • 示例
      • getMethod()
      • getMethods()
  • 带有Declared:可以获取任何权限的属性/方法,但是不可以获取父类的属性/方法
    • 示例
      • getDeclaredMethods():获取本类中任何权限的方法