JavaBase-Java反射机制
Java反射机制概述
概述
- Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法
- 加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,形象的称之为:反射。
动态语言VS静态语言
- 动态语言
- 是一类在运行时可以改变其结构的语言:例如新的函数、对象、甚至代码可以被引进,已有的函数可以被删除或是其他结构上的变化、通俗的说就是在运行时代码可以根据某些条件改变自身结构
- 主要动态语言:Object-c、C#、javascript、PHP、Python、Erlang
- 静态语言
- 与动态语言相对应的,运行时结构不可变的语言就是静态语言。如Java、C、C++
- Java不是动态语言,但是Java可以称之为“准动态语言”、即Java有一定的动态性,我们可以利用反射机制、字节码操作获得类似动态语言的特性。
- Java的动态性让编程的时候更加灵活
Java反射机制研究及应用
- Java反射机制提供的功能
- 在运行时判断任意一个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时判断任意一个类所具有的成员变量和方法
- 在运行时获取泛型信息
- 在运行时调用任意一个对象的成员变量和方法
- 在运行时处理注解
- 生成动态代理
反射相关的主要API
- java.lang.Class:代表一个类
- java.lang.reflect.Method:代表类的方法
- java.lang.reflect.Field:代表类的成员变量
- Java.lang.reflect.Constructor:代表类的构造器
反射的初步例子
-
例子
-
package com.jl.java.base.reflection; import org.junit.Test; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; /** * @author jiangl * @version 1.0 * @date 2021/5/6 13:28 */ public class ReflectionTest { /** * 不使用反射,对于Person的操作 */ @Test public void test1(){ //1.创建Person类的对象 Person person = new Person("Tom",1); //2.通过对象,调用其内部的属性、方法 person.age = 10; System.out.println(person.toString()); person.show(); //在Person类外部,不可以通过Person类的对象调用其内部私有结构。(由于封装性的限制) //比如私有的name属性,私有的showNation(String nation)方法,私有的构造器private Person(String name) } /** * 使用反射 */ @Test public void test2(){ Class clazz = Person.class; try { //1.通过反射创建Person类的对象 Constructor constructor = clazz.getConstructor(String.class, int.class); Person person = (Person) constructor.newInstance("Tom", 123); System.out.println(person); //2.通过反射,调用对象指定的属性、方法 //调属性 Field age = clazz.getField("age"); age.set(person,10); System.out.println(person); //调方法 Method show = clazz.getMethod("show"); Object invoke = show.invoke(person); System.out.println(invoke); //通过反射调用类的 私有的构造器,私有的方法,私有的属性 //调用私有的构造器 Constructor constructor1 = clazz.getDeclaredConstructor(String.class); constructor1.setAccessible(true); Person p1 = (Person) constructor1.newInstance("Jack"); System.out.println(p1); //调用私有的方法 Method showNation = clazz.getDeclaredMethod("showNation", String.class); showNation.setAccessible(true); showNation.invoke(p1,"中国"); Field name = clazz.getDeclaredField("name"); name.setAccessible(true); name.set(p1,"Jacky"); System.out.println(p1); } catch (Exception e) { e.printStackTrace(); } } } class Person{ private String name; public int age; public Person() { } public Person(String name, int age) { this.name = name; this.age = age; } private Person(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public void show(){ System.out.println("你好"); } private void showNation(String nation){ System.out.println("我的国籍是:"+nation); } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
-
思考
- 通过直接new的方式或者反射的方式都可以调用公共的结构,开发中使用哪个?
- 建议:使用new的方式
- 什么时候使用反射的方式,反射的特征:动态性。
- 反射机制与面向对象中的封装性是否矛盾?如何看待两个技术?
- 不矛盾
理解Class类并获取Class实例
关于java.lang.Class类的理解
- 类的加载过程
- 程序在经过javac.ext命令以后,会生成一个或多个字节码文件(.class结尾),接着使用java.ext命令对某个字节码文件进行解释运行。相当于将摸个字节码文件加载到内存中。此过程就成为类的加载。加载到内存中的类,称之为运行时类,此运行时类,就作为Class的一个实例
- Class的实例对应着一个运行时类,此运行时类只有一个(单例模式)
- 加载到内存中的运行时内,会缓存一定的时间,在此时间室内,可以通过不同的方式获取此运行时类
获取Class类实例的方式(前三种需要掌握)
- 方式一:调用运行时类的属性:.class
- Class clazz = Person.class
- 方式二:通过运行时类的对象,调用getClass()
- Person p1 = new Person();
- Class clazz = p1.getClass();
- 方式三:通过Class的静态方式:forName(String classPath) 类的全类名
- Class clazz = Class.forName(“com.jl.java.base.Person”);
- Class clazz = Class.forName(“java.lang.String”);
- 方式四:使用类的加载器:ClassLoader(了解)
- ClassLoader classLoader = ReflectionTest.class.getClassLoader();
- Class clazz = classLoader.loadClass(“com.jl.java.base.Person”);
哪些类型可以有Class对象
-
1.Class:
- 外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类
-
interface:接口
-
[]:数组
-
enum:枚举
-
annotation:注解@annotation
-
primitive type:基本数据类型
-
void
-
public static void main(String[] args) { Class clazz1 = Object.class; Class<Comparable> comparableClass = Comparable.class; Class<String[]> aClass = String[].class; Class<int[][]> aClass1 = int[][].class; Class<ElementType> elementTypeClass = ElementType.class; Class<Override> overrideClass = Override.class; Class<Integer> integerClass = int.class; Class<Void> voidClass = void.class; Class<Class> classClass = Class.class; }
类的加载与ClassLoader的理解
类的加载过程
-
当程序主动使用某个类时,如果该类还未被加载到内存中,则系统会通过如下三个步骤对该类进行初始化
-
-
加载:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区运行时数据结构,然后生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口(即引用地址)。所有需要访问和使用类的数据只能这个在方法区中的Class对象(仅有一个)。这个加载的过程需要类加载器参与
-
链接:将Java类的二进制代码合并到JVM的运行状态之中的过程。
- 验证:确保加载的类信息符合JVM规范,例如:以cafe开头,没有安全方面的问题
- 准备:正式为类的静态变量(static)分配内存并设置类变量默认初始值的阶段,这些内存都在方法区中进行分配
- 解析:虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程。
-
初始化:
- 执行类构造器()方法的过程。类构造器()方法是由编译期自动收集类中所有变量的赋值动作和静态代码块中的语句合并产生的。(类构造器是构造类信息的,不是构造该类对象的构造器)。
- 当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化。
- 虚拟机会保证一个类的()方法在多线程环境中被正确加锁和同步。
类的加载器
-
类的加载器的作用:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后在堆中生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口
-
类缓存:标准的JavaSE类加载器可以按要求查找类,但一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间。不过JVM垃圾回收机制可以回收这邪恶Class对象
-
-
了解:ClassLoader
-
类加载器作用是把类(class)装载进内存的。JVM规范定义了如下类型的类的加载器
-
-
@Test public void test(){ //对于自定义类,使用系统类加载器进行加载 System.out.println(ClassLoaderTest.class.getClassLoader()); //调用系统类加载器的getParent():获取扩展类加载器 System.out.println(ClassLoaderTest.class.getClassLoader().getParent()); //调用扩展类加载器的getParent():无法获取引导类加载器 System.out.println(ClassLoaderTest.class.getClassLoader().getParent().getParent()); }
-
使用ClassLoader加载配置文件
@Test
public void test2() throws Exception {
//此时的文件默认在当前的module下
Properties pros = new Properties();
//读取配置文件的方式一:
FileInputStream fis = new FileInputStream(new File("jdbc.properties"));
pros.load(fis);
String user = pros.getProperty("user");
//读取配置文件的方式二:使用ClassLoader
//配置文件默认识别为:当前module的src下
Properties pros1 = new Properties();
ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
InputStream resourceAsStream = classLoader.getResourceAsStream("jdbc1.properties");
pros1.load(resourceAsStream);;
System.out.println(pros1.getProperty("name"));
}
创建运行时类的对象
-
调用newInstance()方法创建运行时类的对象:
- 调用此方法,创建对应的运行时类的对象。内部调用了运行时类的空参的构造器
-
要想使用newInstance()方法正常的创建运行时类的对象,要求:
- 运行时类必须提供空参的构造器
- 空参的构造器的访问权限合适,通常,设置为public
-
在Java Bean中要求提供一个public 的空参构造器,原因:
- 便于通过反射,创建运行时类的对象
- 便于子类继承此运行时类时,默认调用super()时,保证父类有此构造器
-
/** * @author jiangl * @version 1.0 * @date 2021/5/6 16:46 */ public class NewInstanceTest { @Test public void test1() throws IllegalAccessException, InstantiationException { //newInstance():调用方法,创建对应的运行时类的喜爱那个,内部调用了运行时类的空参的构造器 Class<Person> clazz = Person.class; Person person = clazz.newInstance(); System.out.println(person); } }
体会反射的动态性
@Test
public void test3(){
for(int i=0;i<10;i++){
try {
test2();
} catch (Exception e) {
e.printStackTrace();
}
}
}
@Test
public void test2() throws Exception {
int i = new Random().nextInt(3);
String classPath = "";
switch(i){
case 0:
classPath = "java.util.Date";
break;
case 1:
classPath = "java.sql.Date";
break;
case 2:
classPath = "com.jl.java.base.reflection.Person";
break;
}
Object obj = getInstance(classPath);
System.out.println(obj);
}
/**
* 创建一个指定类的对象
* @param classPath 指定类的全类名
* @return
*/
public Object getInstance(String classPath) throws Exception {
Class<?> aClass = Class.forName(classPath);
return aClass.newInstance();
}
获取运行时类的完整结构
-
获取运行时类的属性
-
getFields():获取运行时类及其父类声明的public修饰的属性
-
getDeclaredFields():获取运行时类中声明的所有属性。(不包括父类中声明的属性)
-
@Test public void test(){ Class<Person> aClass = Person.class; //获取属性结构 //getFields():获取当前运行时类及其父类中声明的public访问权限的属性 Field[] fields = aClass.getFields(); for(Field field : fields){ System.out.println(field); } //getDeclaredFields():获取当前运行时类中声明的所有属性。(不包含父类中声明的属性) System.out.println(); Field[] declaredFields = aClass.getDeclaredFields(); for(Field field :declaredFields){ System.out.println(field); } }
-
获取到属性后的操作
-
/** * 属性中有权限修饰符,数据类型,变量名 */ @Test public void test2(){ Class<Person> aClass = Person.class; Field[] declaredFields = aClass.getDeclaredFields(); for(Field field :declaredFields){ //1.权限修饰符 Modifiers System.out.print(field.getModifiers()+"\t"); System.out.print(Modifier.toString(field.getModifiers())+"\t"); //2.数据类型 Class<?> type = field.getType(); System.out.print(type.getName()+"\t"); System.out.print(type); String name = field.getName(); System.out.print(name); System.out.println(); } }
-
-
-
获取运行时类的方法
-
getMethods():获取运行时类及其父类声明的public修饰符修饰的方法
-
getDeclaredMethods():获取运行时类中声明的所有方法。(不包括父类中声明的方法)
-
@Testpublic void test(){ Class<Person> aClass = Person.class; //获取属性结构 //getFields():获取当前运行时类及其父类中声明的public访问权限的属性 Field[] fields = aClass.getFields(); for(Field field : fields){ System.out.println(field); } //getDeclaredFields():获取当前运行时类中声明的所有属性。(不包含父类中声明的属性) System.out.println(); Field[] declaredFields = aClass.getDeclaredFields(); for(Field field :declaredFields){ System.out.println(field); }}
-
获取运行时类的方法后的操作
-
/** * 属性中有权限修饰符,数据类型,变量名 */@Testpublic void test2(){ Class<Person> aClass = Person.class; Field[] declaredFields = aClass.getDeclaredFields(); for(Field field :declaredFields){ //1.权限修饰符 Modifiers System.out.print(field.getModifiers()+"\t"); System.out.print(Modifier.toString(field.getModifiers())+"\t"); //2.数据类型 Class<?> type = field.getType(); System.out.print(type.getName()+"\t"); System.out.print(type); String name = field.getName(); System.out.print(name); System.out.println(); }}
-
-
-
获取运行时类的构造器
-
getConstructors():获取运行时类的public修饰的构造器。(不包含父类声明的构造器)
-
getDeclaredConstructors():获取运行时类中所有的声明的构造器。(不包含父类声明的构造器)
-
/** * 获取类的构造器 */@Testpublic void test1(){ Class<Person> clazz = Person.class; //getConstructors():获取当前运行时类的声明的构造函数 Constructor<?>[] constructors = clazz.getConstructors(); for(Constructor constructor : constructors){ System.out.println(constructor); } System.out.println(); //getDeclaredConstructors():获取当前运行时类的所有构造器 Constructor<?>[] declaredConstructors = clazz.getDeclaredConstructors(); for(Constructor constructor : declaredConstructors){ System.out.println(constructor); }}
-
-
获取运行时类的父类
-
getSuperClass():获取父类
-
getGenericSuperClass():获取带泛型的父类
-
/** * 获取父类 */@Testpublic void test2(){ Class<Person> clazz = Person.class; System.out.println(clazz.getSuperclass());}/** * 获取带泛型父类 */@Testpublic void test3(){ Class<Person> clazz = Person.class; System.out.println(clazz.getGenericSuperclass());}
-
-
获取运行时类实现的接口
-
getInterface()
-
/** * 获取运行时类实现的接口 */@Testpublic void test5(){ Class<Person> clazz = Person.class; Class<?>[] interfaces = clazz.getInterfaces(); for(Class i : interfaces){ System.out.println(i); }}
-
-
获取运行时类所在的包
-
getPackage()
-
/** * 获取运行时类所在的包 */@Testpublic void test6(){ Class<Person> clazz = Person.class; System.out.println(clazz.getPackage());}
-
-
获取运行时类声明的注解
-
getAnnotations()
-
/** * 获取运行时类声明的注解 */@Testpublic void test7(){ Class<Person> clazz = Person.class; Annotation[] annotations = clazz.getAnnotations(); for(Annotation annotation : annotations){ System.out.println(annotation); }}
-
调用运行时类的指定结构
-
获取运行时类的指定的属性
-
步骤
- 创建所要操作的一个Class类对象Class clazz = Person.class
- 调用getDeclaredField(String fieldName)方法获取到,所要操作的属性对应的Field对象
- 调用Field.setAccessiable(boolean flag),将属性设置为可操作
- 调用set(Object o,Object val),参数1:指明设置哪个对象的属性 参数2:将此属性设置成多少。这是对象的属性值
-
/** * */@Testpublic void testField(){ Class<Person> clazz = Person.class; try { Person person = clazz.newInstance(); //获取指定的属性 Field name = clazz.getField("id"); /** * 设置当前属性的值 * set():参数1:指明设置哪个对象的属性 参数2:将此属性设置成多少 */ name.set(person,101); /** * 获取当前对象属性的值:要求运行时类的属性是public的 */ int pid = (Integer) name.get(person); System.out.println(pid); } catch (Exception e) { e.printStackTrace(); }}@Testpublic void test1() throws Exception { Class<Person> clazz = Person.class; Person o = clazz.newInstance(); //1.getDeclaredField(String fieldName):获取运行时类的指定变量名的属性 Field name = clazz.getDeclaredField("name"); //2.保证当前属性是可访问的 name.setAccessible(true); //3.获取设置对象的属性值 name.set(o,"Tom"); System.out.println(o);}
-
-
获取运行时类指定的方法
-
步骤
- 创建所要操作的一个Class类对象Class clazz = Person.class
- 调用getDeclaredMethod(String fieldName)方法获取到,所要操作的方法对应的Method对象
- 调用Method.setAccessiable(boolean flag),将方法设置为可操作
- 调用method.invoke()参数1 方法的调用者, 参数2 ,给方法形参传递值的实参,调用方法
- invoke()方法的返回值即为对应类中调用方法的返回值
- 注意:静态方法的调用invoke()的参数1可以传任意对象
-
@Testpublic void testMethod() throws Exception { Class<Person> clazz = Person.class; Person o = clazz.newInstance(); /** * 1.获取指定的某个方法 * getDeclaredMethod():参数1:指明获取的方法名 参数2:指明获取方法的形参列表 */ Method show = clazz.getDeclaredMethod("show", String.class); //2.保证当前方法是可访问的 show.setAccessible(true); /** * 3.invoke():参数1 方法的调用者, 参数2 ,给方法形参传递值的实参 * invoke()方法的返回值即为对应类中调用方法的返回值 */ Object result = show.invoke(o, "dfasdf"); System.out.println(result); Method showDesc = clazz.getDeclaredMethod("showDesc"); showDesc.setAccessible(true); //如果调用运行时类中的方法没有返回值,则invoke()方法返回null Object invoke = showDesc.invoke(o); System.out.println(invoke);}
-
-
获取运行时类的指定的构造器
-
步骤
- 创建所要操作的一个Class类对象Class clazz = Person.class
- 调用getDeclaredConstructor(String fieldName)方法获取到,所要操作的构造器对应的Constructor对象
- 调用Constructor.setAccessiable(boolean flag),将构造器设置为可操作
- 调用Constructor.newInstance() 创建对象
-
@Test public void testConstructor()throws Exception { Class<Person> clazz = Person.class; Person o = clazz.newInstance(); //1.获取指定的构造器 Constructor<Person> constructor = clazz.getDeclaredConstructor(String.class); //2.保证此构造器是可访问的 constructor.setAccessible(true); //3.调用此构造器创建运行时类的对象 Person tom = constructor.newInstance("Tom"); System.out.println(tom); }
-
反射的应用:动态代理
动态代理概述
- 代理设计模式的原理:
- 使用一个代理将对象包装起来,然后用该代理对象代替原始对象。任何对原始对象的调用都要通过代理对象操作。代理对象决定是否以及何时将方法调用转到原始对象上
- 静态代理:
- 特征是代理类和目标对象的类都是在编译期间确定下来,不利于程序的扩展。同时,每一个代理类只能为一个接口服务,这样导致程序开发中必然产生过多的代理类。理想的方式是,通过一个代理类完成完全的代理功能。
- 动态代理是指客户通过代理类来调用其他对象的方法,并且是在程序运行时根据需要动态创建目标类的代理对象。
- 动态代理适用场合:
- 调试
- 远程方法调用
- 动态代理相比于静态代理的有点:
- 抽象角色中(接口)声明的所有方法都被转移到调用处理器一个集中的方法中处理,这样,可以更加灵活和统一的处理众多的方法
动态代理的例子
静态代理
-
步骤
- 创建一个被接口类ClothFactory,定义所需要代理的方法
- 创建被代理类MyClothFactory实现接口ClothFactory
- 创建被代理类ProxyClothFactory实现接口ClothFactory
- 在代理类中,添加代理类MyClothFactory的属性 ,作为后续从代理类控制何时调用被代理类
- 在代理类中,添加给代理类属性赋值的方法
- 在代理类的代理方法中,通过被代理类的属性调用被代理类的方法
-
代码
-
package com.jl.java.base.proxy; /** * 静态代理 * 特点:代理类和被代理类在编译期间,就确定下来了 * @author jiangl * @version 1.0 * @date 2021/5/7 15:14 */ /** * 1.创建一个接口,使得代理类和被代理类都实现这个接口,创建一个被接口类ClothFactory,定义所需要代理的方法 */ interface ClothFactory{ void produceCloth(); } /** * 2.创建被代理类MyClothFactory实现接口ClothFactory */ class MyClothFactory implements ClothFactory{ @Override public void produceCloth() { System.out.println("生产衣服"); } } /** * 3.创建被代理类ProxyClothFactory实现接口ClothFactory */ class ProxyClothFactory implements ClothFactory{ /** * 4.在代理类中,添加代理类MyClothFactory的属性 ,作为后续从代理类控制何时调用被代理类 */ private ClothFactory myClothFactory; /** * 5.在代理类中,添加给代理类属性赋值的方法 * @param myClothFactory */ public ProxyClothFactory(MyClothFactory myClothFactory) { this.myClothFactory = myClothFactory; } /** * 6.在代理类的代理方法中,通过被代理类的属性调用被代理类的方法 */ @Override public void produceCloth() { System.out.println("获取原料"); myClothFactory.produceCloth(); System.out.println("完成"); } } public class StaticProxyTest { public static void main(String[] args) { ClothFactory proxyClothFactory = new ProxyClothFactory(new MyClothFactory()); proxyClothFactory.produceCloth(); } }
-
-
静态代理缺点
- 在编译期,就确定了代理类和被代理类,如果大量使用会导致系统中存在很多代理类
动态代理(JDK动态代理)
-
创建一个被接口类Human,定义所需要代理的方法,代理类和被代理类都要实现这个方法
-
创建一个被代理类Student实现Human接口,并重写接口中的方法
-
通过Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)方法,生成代理类
- ClassLoader loader:被代理类的类加载器,用于通过类加载器动态创建一个代理类
- Class<?>[] interfaces:被代理类所实现的所有接口数组,因为被代理类可能实现多个接口所以是数组。而代理类需要通过实现这些接口来,并重写接口中的方法来代理被代理类。
- InvocationHandler h:回调方法对象,代理类通过回调方法对象中的 invoke方法来实现对被代理类的调用和功能的增强(可以在invoke(Object proxy, Method method, Object[] args)中编写所要对被代理类功能增强的代码逻辑)
- Object proxy:这是代理类的对象
- Method method:这是所要代理的方法
- Object[] args:所要代理方法的入参数组
- 在InvocationHandler h类中需要添加一个被代理类的属性(为了通用性该属性的类型应为Object),用于在invoke()方法中,通过Method.invoke方法来调用被代理类的所需要代理的方法
-
最后先创建一个被代理类对象
- 通过Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)方法获取到代理类的对象,该对象可以赋值给Human proxy(向上转型,类的多态性)
- Human proxy = Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h);
- 通过调用代理类对象proxy.eat()调用被代理类的方法,实现代理模式
- 通过Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)方法获取到代理类的对象,该对象可以赋值给Human proxy(向上转型,类的多态性)
-
代码
-
package com.jl.java.base.proxy; import org.junit.Test; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * @author jiangl * @version 1.0 * @date 2021/5/7 15:19 */ interface Human{ String getBelief(); void eat(String food); } /** * 被代理类 */ class Student implements Human{ @Override public String getBelief() { System.out.println("好好学习,天天向上"); return "学习"; } @Override public void eat(String food) { System.out.println("吃有营养的东西"); } } /** * 要想实现动态代理,需要解决的问题? * 问题一:如何根据加载到内存中的被代理类,动态的创建一个代理类及其对象 * 问题二:当通过代理类的对象调用方法时,如何动态的去调用被代理类中的同名方法 */ class ProxyFactory{ /** * 调用此方法,返回一个代理类的对象,解决问题一 * @param obj 被代理类的对象 * @return 返回代理类的对象 */ public static Object getProxyInstance(Object obj){ MyInvocationHandler handler = new MyInvocationHandler(); handler.bind(obj); return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), handler); } } class MyInvocationHandler implements InvocationHandler{ /** * 需要使用被代理类的对象进行赋值 */ private Object obj; public void bind(Object obj){ this.obj = obj; } /** * 当通过代理类的对象,调用方法a时,就会自动的调用如下的方法 * 将被代理类要执行的方法a的功能就声明在invoke()中 * @param proxy 代理类的对象 * @param method 代理类对象调用的方法,此方法也就作为了被代理类对象要调用的方法 * @param args 方法的入参 * @return * @throws Throwable */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("---代理---"); //obj:被代理类对象 Object returnValue = method.invoke(obj, args); //上述方法的返回值,就作为当前类的invoke()的返回值 return returnValue; } } public class DynamicProxyTest { @Test public void test(){ Object proxyInstance1 = ProxyFactory.getProxyInstance(new Student()); System.out.println(proxyInstance1.getClass()); Class<?>[] interfaces = proxyInstance1.getClass().getInterfaces(); for(Class inter : interfaces){ System.out.println(inter); } Human proxyInstance = (Human) ProxyFactory.getProxyInstance(new Student()); proxyInstance.getBelief(); proxyInstance.eat("苹果"); } }
-
源码解析(JDK动态代理)
-
前提:
-
通过Proxy.nexProxyInstance()方法创建的类,实际是实现了被代理类接口,并且继承与Proxy类的如$Proxy0匿名类
-
代码
-
@Test public void test(){ Object proxyInstance1 = ProxyFactory.getProxyInstance(new Student()); System.out.println(proxyInstance1.getClass()); System.out.println(proxyInstance1.getClass().getSuperclass()); Class<?>[] interfaces = proxyInstance1.getClass().getInterfaces(); for(Class inter : interfaces){ System.out.println(inter); } }
-
结果
- class com.jl.java.base.proxy.$Proxy5
class java.lang.reflect.Proxy
interface com.jl.java.base.proxy.Human
- class com.jl.java.base.proxy.$Proxy5
-
-
源码:
-
生成代理类源码newProxyInstance()方法
-
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException { //判断回调方法对象是否为空 if (h == null) { throw new NullPointerException(); } /** * 查找或生成指定的代理类。 */ Class<?> cl = getProxyClass0(loader, interfaces); /* * 通过代理类的构造器,反射生成代理类,并将需要调用的方法写入代理类 */ try { final Constructor<?> cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h; SecurityManager sm = System.getSecurityManager(); if (sm != null && java.lang.reflect.Proxy.ProxyAccessHelper.needsNewInstanceCheck(cl)) { return AccessController.doPrivileged(new PrivilegedAction<Object>() { public Object run() { return newInstance(cons, ih); } }); } else { return newInstance(cons, ih); } } catch (NoSuchMethodException e) { throw new InternalError(e.toString()); } }
-
-
在newProxyInstance()方法中,通过getProxyClass0(loader,interfaces)方法生成匿名代理类
-
通过Java安全器检测
-
//Java安全器检测 SecurityManager sm = System.getSecurityManager(); if (sm != null) { final int CALLER_FRAME = 3; // 0: Reflection, 1: getProxyClass0 2: Proxy 3: caller final Class<?> caller = Reflection.getCallerClass(CALLER_FRAME); final ClassLoader ccl = caller.getClassLoader(); checkProxyLoader(ccl, loader); ReflectUtil.checkProxyPackageAccess(ccl, interfaces); }
-
-
判断代理类的实现接口是否超过了int最大值,如果超过则抛出异常IllegalArgumentException
-
//判断被代理类的接口是否超过int最大值 if (interfaces.length > 65535) { throw new IllegalArgumentException("interface limit exceeded"); }
-
-
创建一个代理类的变量Class proxyClass用于返回
-
//代理类 变量 Class proxyClass = null;
-
-
获取被代理类的所有接口(接口数组),将这些接口名作为代理类缓存的key。用于再次获取时不需要二次创建,优化性能
-
//获取所有的接口名称用于作为代理类缓存的关键字 String[] interfaceNames = new String[interfaces.length]; //创建一个被代理类接口的HashSet用于去重 Set interfaceSet = new HashSet(); //循环遍历入参代理类的所有接口 for (int i = 0; i < interfaces.length; i++) { //检查通过类加载加载的接口实际类,是否和传入的接口类相同 String interfaceName = interfaces[i].getName(); Class interfaceClass = null; try { interfaceClass = Class.forName(interfaceName, false, loader); } catch (ClassNotFoundException e) { } if (interfaceClass != interfaces[i]) { throw new IllegalArgumentException( interfaces[i] + " is not visible from class loader"); } //校验加载得到的接口类是否真实为接口 if (!interfaceClass.isInterface()) { throw new IllegalArgumentException( interfaceClass.getName() + " is not an interface"); } //判断是否有重复接口 if (interfaceSet.contains(interfaceClass)) { throw new IllegalArgumentException( "repeated interface: " + interfaceClass.getName()); } interfaceSet.add(interfaceClass); interfaceNames[i] = interfaceName; }
-
-
将接口数组转换成List,作为key
-
//将接口名字符数组转换成List<String> /** * 使用代理接口的字符串表示作为代理类缓存中的键(而不是它们的类对象)就足够了, * 因为我们要求代理接口通过提供的类加载器按名称进行解析,它的优点是,使用类的字符串表示会导致对类的隐式弱引用。 */ Object key = Arrays.asList(interfaceNames);
-
-
根据类加载器在缓存loaderToCache map中查询,这个类加载下的所有代理类的Map
-
WeakHashMap<ClassLoader,HashMap<List,Object>>
-
//通过类加载器找到或创建一个代理类缓存 Map cache; synchronized (loaderToCache) { //根据类加载器找到一个缓存Map cache = (Map) loaderToCache.get(loader); //如果类加载器的缓存map不存在,则重新创建 if (cache == null) { cache = new HashMap(); loaderToCache.put(loader, cache); } /** * 此映射将在此方法期间保持有效,无需进一步同步,因为只有在类装入器变得不可访问时才会删除映射。 */ }
-
-
获取的到代理类map中根据,接口数组key查询获取代理类,此处有三种情况
-
根据key查询到代理类,直接返回
-
根据key获取到对象pendingGenerationMarker,表示代理类正在创建中,调用cache.await()方法进入等待
-
根据key获取到null,则表示未创建代理,此时先将key的value赋值成pendingGenerationMarker,防止多次创建,保证线程安全,其他线程来访问则会进入上一个分支,进入等待(而正真的创建代理类,在后续逻辑中)
-
/** * 使用键在代理类缓存中查找接口列表。此查找将导致三种可能的值之一: * null:类加载器中的接口列表当前没有代理类 * the pendingGenerationMarker object: 当前正在生成接口列表的代理类 * a weak reference to a Class object: 接口列表的代理类已经生成。 */ synchronized (cache) { /** * 请注意,我们不必担心在map中清除弱引用条目的缓存, * 因为如果代理类已被垃圾收集,则其类装入器也将被垃圾收集, * 因此这个缓存在GC后从整个代理类加载器缓存Map中清除 */ do { //从缓存中根据接口字符串做的关键字key获取代理类 Object value = cache.get(key); //判断如果获取到的值是Reference类的对象,则调用Reference对象中的get()方法获取,引用的实际对象 if (value instanceof Reference) { proxyClass = (Class) ((Reference) value).get(); } //判断代理类 if (proxyClass != null) { //代理类已创建,返回代理类 return proxyClass; } else if (value == pendingGenerationMarker) { // 代理类正在创建中,进入wait()等待 try { cache.wait(); } catch (InterruptedException e) { /* * The class generation that we are waiting for should * take a small, bounded time, so we can safely ignore * thread interrupts here. */ } continue; } else { /** * 尚未生成或正在生成此接口列表的代理类,因此我们将立即生成它。将其标记为挂起生成。 * 这样做的目的是,多线程情况下,其他线程会看到一个正在创建的标识,进入上一个if条件,进入等待 */ cache.put(key, pendingGenerationMarker); break; } } while (true); }
-
-
创建代理类对象步骤
-
判断被代理类的接口是否都在同一个包名下,校验不通过在抛异常返回
-
/** * 记录非公共代理接口的包,以便在同一个包中定义代理类。 * 验证所有非公共代理接口是否在同一个包中。 */ for (int i = 0; i < interfaces.length; i++) { int flags = interfaces[i].getModifiers(); if (!Modifier.isPublic(flags)) { String name = interfaces[i].getName(); int n = name.lastIndexOf('.'); String pkg = ((n == -1) ? "" : name.substring(0, n + 1)); if (proxyPkg == null) { proxyPkg = pkg; } else if (!pkg.equals(proxyPkg)) { throw new IllegalArgumentException( "non-public interfaces from different packages"); } } }
-
生成一个代理类的名称,规则是 包名+jdk代理类前缀$Proxy+计数
-
//生成一个代理类的名称,规则是 包名+jdk代理类前缀$Proxy+计数 long num; synchronized (nextUniqueNumberLock) { num = nextUniqueNumber++; } String proxyName = proxyPkg + proxyClassNamePrefix + num;
-
调用 ProxyGenerator.generateProxyClass方法生成代理类文件
-
//生成一个代理类文件 byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces);
-
通过类加载器,代理类名,代理类文件,生成一个代理类的类对象
-
try { //通过类加载器,代理类名,代理类文件,生成一个代理类的类对象 proxyClass = defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length); } catch (ClassFormatError e) { /* * A ClassFormatError here means that (barring bugs in the * proxy class generation code) there was some other * invalid aspect of the arguments supplied to the proxy * class creation (such as virtual machine limitations * exceeded). */ throw new IllegalArgumentException(e.toString()); }
-
将代理类添加到proxyClasses Map中,用于isProxyClass方法中使用,判断类是否为代理类
-
//将代理类添加到proxyClasses Map中,用于isProxyClass方法中使用,判断类是否为代理类 // add to set of all generated proxy classes, for isProxyClass proxyClasses.put(proxyClass, null);
-
-
最后在fianlly代码块中做最后的处理,将之前设置的缓存key,根据是否正确生成代理做最后赋值
-
如果代理类创建成功,则创建一个弱引用WeakReference把代理类对象引用,把弱引用作为value赋值给接口名数组的key
-
如果代理类创建失败,则删除key
-
最后唤醒所有等待队列中的线程
-
/** * 我们必须以某种方式清除代理类缓存项的“挂起的生成”状态。 * 如果成功生成了代理类,则将其存储在缓存中(使用弱引用);否则,请删除保留项。 * 在所有情况下,都要通知此缓存中保留项的所有等待队列中的线程。 */ synchronized (cache) { if (proxyClass != null) { //代理类创建成功 //根据接口数组 作为key,将代理类封装成一个索引用放入缓存map中 cache.put(key, new WeakReference(proxyClass)); } else { //代理类创建失败,则在缓存中删除key cache.remove(key); } //唤醒其他等待的线程, cache.notifyAll(); }
-
-
源码
-
private static Class<?> getProxyClass0(ClassLoader loader, Class<?>... interfaces) { //Java安全器检测 SecurityManager sm = System.getSecurityManager(); if (sm != null) { final int CALLER_FRAME = 3; // 0: Reflection, 1: getProxyClass0 2: Proxy 3: caller final Class<?> caller = Reflection.getCallerClass(CALLER_FRAME); final ClassLoader ccl = caller.getClassLoader(); checkProxyLoader(ccl, loader); ReflectUtil.checkProxyPackageAccess(ccl, interfaces); } //判断被代理类的接口是否超过int最大值 if (interfaces.length > 65535) { throw new IllegalArgumentException("interface limit exceeded"); } //代理类 变量 Class proxyClass = null; //获取所有的接口名称用于作为代理类缓存的关键字 String[] interfaceNames = new String[interfaces.length]; //创建一个被代理类接口的HashSet用于去重 Set interfaceSet = new HashSet(); //循环遍历入参代理类的所有接口 for (int i = 0; i < interfaces.length; i++) { //检查通过类加载加载的接口实际类,是否和传入的接口类相同 String interfaceName = interfaces[i].getName(); Class interfaceClass = null; try { interfaceClass = Class.forName(interfaceName, false, loader); } catch (ClassNotFoundException e) { } if (interfaceClass != interfaces[i]) { throw new IllegalArgumentException( interfaces[i] + " is not visible from class loader"); } //校验加载得到的接口类是否真实为接口 if (!interfaceClass.isInterface()) { throw new IllegalArgumentException( interfaceClass.getName() + " is not an interface"); } //判断是否有重复接口 if (interfaceSet.contains(interfaceClass)) { throw new IllegalArgumentException( "repeated interface: " + interfaceClass.getName()); } interfaceSet.add(interfaceClass); interfaceNames[i] = interfaceName; } //将接口名字符数组转换成List<String> /** * 使用代理接口的字符串表示作为代理类缓存中的键(而不是它们的类对象)就足够了, * 因为我们要求代理接口通过提供的类加载器按名称进行解析,它的优点是,使用类的字符串表示会导致对类的隐式弱引用。 */ Object key = Arrays.asList(interfaceNames); //通过类加载器找到或创建一个代理类缓存 Map cache; synchronized (loaderToCache) { //根据类加载器找到一个缓存Map cache = (Map) loaderToCache.get(loader); //如果类加载器的缓存map不存在,则重新创建 if (cache == null) { cache = new HashMap(); loaderToCache.put(loader, cache); } /** * 此映射将在此方法期间保持有效,无需进一步同步,因为只有在类装入器变得不可访问时才会删除映射。 */ } /** * 使用键在代理类缓存中查找接口列表。此查找将导致三种可能的值之一: * null:类加载器中的接口列表当前没有代理类 * the pendingGenerationMarker object: 当前正在生成接口列表的代理类 * a weak reference to a Class object: 接口列表的代理类已经生成。 */ synchronized (cache) { /** * 请注意,我们不必担心在map中清除弱引用条目的缓存, * 因为如果代理类已被垃圾收集,则其类装入器也将被垃圾收集, * 因此这个缓存在GC后从整个代理类加载器缓存Map中清除 */ do { //从缓存中根据接口字符串做的关键字key获取代理类 Object value = cache.get(key); //判断如果获取到的值是Reference类的对象,则调用Reference对象中的get()方法获取,引用的实际对象 if (value instanceof Reference) { proxyClass = (Class) ((Reference) value).get(); } //判断代理类 if (proxyClass != null) { //代理类已创建,返回代理类 return proxyClass; } else if (value == pendingGenerationMarker) { // 代理类正在创建中,进入wait()等待 try { cache.wait(); } catch (InterruptedException e) { /* * The class generation that we are waiting for should * take a small, bounded time, so we can safely ignore * thread interrupts here. */ } continue; } else { /** * 尚未生成或正在生成此接口列表的代理类,因此我们将立即生成它。将其标记为挂起生成。 * 这样做的目的是,多线程情况下,其他线程会看到一个正在创建的标识,进入上一个if条件,进入等待 */ cache.put(key, pendingGenerationMarker); break; } } while (true); } //开始生产代理类 try { String proxyPkg = null; // package to define proxy class in /** * 记录非公共代理接口的包,以便在同一个包中定义代理类。 * 验证所有非公共代理接口是否在同一个包中。 */ for (int i = 0; i < interfaces.length; i++) { int flags = interfaces[i].getModifiers(); if (!Modifier.isPublic(flags)) { String name = interfaces[i].getName(); int n = name.lastIndexOf('.'); String pkg = ((n == -1) ? "" : name.substring(0, n + 1)); if (proxyPkg == null) { proxyPkg = pkg; } else if (!pkg.equals(proxyPkg)) { throw new IllegalArgumentException( "non-public interfaces from different packages"); } } } //如果未传入接口或者未找到包名,则使用默认包名com.sun.proxy. if (proxyPkg == null) { // if no non-public proxy interfaces, use com.sun.proxy package proxyPkg = ReflectUtil.PROXY_PACKAGE + "."; } { //生成一个代理类的名称,规则是 包名+jdk代理类前缀$Proxy+计数 long num; synchronized (nextUniqueNumberLock) { num = nextUniqueNumber++; } String proxyName = proxyPkg + proxyClassNamePrefix + num; /** * 校验是否已经加载了代理类,这一步好像是没有逻辑 * Verify that the class loader hasn't already * defined a class with the chosen name. */ //生成一个代理类 byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces); try { proxyClass = defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length); } catch (ClassFormatError e) { /* * A ClassFormatError here means that (barring bugs in the * proxy class generation code) there was some other * invalid aspect of the arguments supplied to the proxy * class creation (such as virtual machine limitations * exceeded). */ throw new IllegalArgumentException(e.toString()); } } //将代理类添加到proxyClasses Map中,用于isProxyClass方法中使用,判断类是否为代理类 // add to set of all generated proxy classes, for isProxyClass proxyClasses.put(proxyClass, null); } finally { /** * 我们必须以某种方式清除代理类缓存项的“挂起的生成”状态。 * 如果成功生成了代理类,则将其存储在缓存中(使用弱引用);否则,请删除保留项。 * 在所有情况下,都要通知此缓存中保留项的所有等待队列中的线程。 */ synchronized (cache) { if (proxyClass != null) { //代理类创建成功 //根据接口数组 作为key,将代理类封装成一个索引用放入缓存map中 cache.put(key, new WeakReference(proxyClass)); } else { //代理类创建失败,则在缓存中删除key cache.remove(key); } //唤醒其他等待的线程, cache.notifyAll(); } } return proxyClass; }
-
-