文章目录
- 1、反射
- 1.1 反射的概述
- 1.2 反射作用
- 1.3 获取字节码文件对象的方式
- 1.4 字节码文件和字节码文件对象
- 1.5 获取构造方法
- 1.6 获取构造方法并创建对象
- 1.7 获取成员变量并获取值和修改值
- 1.8 获取成员方法
- 1.9 获取成员方法并运行
- 1.10 反射练习
- 1.10.1 泛型擦除
- 1.10.2 修改字符串的内容
- 1.10.3 利用反射保存对象中的信息
- 1.10.4反射结合配置文件
- 2. 动态代理
- 2.1 动态代理好处
- 2.2 动态代理三要素
- 2.3 动态代理简单实现
- 2.4 动态代理扩展
- 2.5 动态代理练习
1、反射
1.1 反射的概述
反射允许对成员变量(字段),成员方法和构造方法的信息进行编程访问。
专业的解释
- 是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;
- 对于任意一个对象,都能够调用它的任意属性和方法;
- 这种动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制。
通俗的理解
-
利用反射创建的对象可以无视修饰符调用类里面的内容
-
可以跟配置文件结合起来使用,把要创建的对象信息和方法写在配置文件中。
-
读取到什么类,就创建什么类的对象
-
读取到什么方法,就调用什么方法
-
此时当需求变更的时候不需要修改代码,只要修改配置文件即可。
1.2 反射作用
反射都是从class字节码文件中获取的内容。
- 获取class字节码文件的对象
- 利用反射如何获取构造方法(创建对象)
- 利用反射如何获取成员变量(赋值,获取值)
- 利用反射如何获取成员方法(运行)
反射的好处:
- 无视修饰符访问类中的内容。但是这种操作在开发中一般不用,都是框架底层来用的;
- 反射可以跟配置文件结合起来使用,动态的创建对象,动态的调用方法。
1.3 获取字节码文件对象的方式
- Class这个类里面的静态方法forName(“全类名”)(最常用)
- 通过class属性获取,一般更多的是当做参数进行传递
- 通过对象获取字节码文件对象,当我们已经有了这个类的对象时,才可以使用。
代码示例:
public class ReflectDemo {public static void main(String[] args) throws ClassNotFoundException {//1. 第一种方式//全类名 : 包名 + 类名Class clazz1 = Class.forName("com.ya.reflect.demo01.Student");//2. 第二种方式Class clazz2 = Student.class;//3.第三种方式Student s = new Student();Class clazz3 = s.getClass();System.out.println(clazz1 == clazz2);System.out.println(clazz2 == clazz3);}
}
1.4 字节码文件和字节码文件对象
java文件(源代码阶段):就是编写的java代码。
字节码文件(加载阶段):就是通过java文件编译之后的class文件(是在硬盘上真实存在的,用眼睛能看到的)
字节码文件对象(运行阶段):当class文件加载到内存之后,虚拟机自动创建出来的对象。这个对象里面至少包含了:构造方法,成员变量,成员方法。
反射获取的是字节码文件对象,这个对象在内存中是唯一的。
1.5 获取构造方法
规则:
- get表示获取
- Declared表示私有
- 最后的s表示所有,复数形式
- 如果当前获取到的是私有的,必须要临时修改访问权限setAccessible(true),否则无法使用
方法名 | 说明 |
---|---|
Constructor<?>[] getConstructors() | 获得所有的构造(只能public修饰) |
Constructor<?>[] getDeclaredConstructors() | 获得所有的构造(包含private修饰) |
Constructor getConstructor(Class<?>… parameterTypes) | 获取指定构造(只能public修饰) |
Constructor getDeclaredConstructor(Class<?>… parameterTypes) | 获取指定构造(包含private修饰) |
Constructor类中用于创建对象的方法:
- T newInstance(Object... initargs) :根据指定的构造方法创建对象
- setAccessible(boolean flag) :设置为true,表示取消访问检查
代码示例:
public class Student {private String name;private int age;public Student() {}public Student(String name) {this.name = name;}protected Student(int age) {this.age = age;}private Student(String name, int age) {this.name = name;this.age = age;}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 String toString() {return "Student{name = " + name + ", age = " + age + "}";}
}
public class ReflectDemo {public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {//1.获取class字节码文件对象Class clazz = Class.forName("com.ya.reflect.demo02.Student");//2.获取构造方法// 2.1 返回所有公共构造方法对象的数组System.out.println("-----返回所有公共构造方法对象的数组----");Constructor[] con1 = clazz.getConstructors();for (Constructor con : con1) {System.out.println(con);}// 2.2 返回所有构造方法对象的数组System.out.println("-------返回所有构造方法对象的数组------");Constructor[] con2 = clazz.getDeclaredConstructors();for (Constructor con : con2) {System.out.println(con);}// 2.3 返回所有构造方法对象的数组System.out.println("-------返回单个公共构造方法对象------");Constructor con3 = clazz.getConstructor(String.class);System.out.println(con3);//2.4 获取指定的空参构造System.out.println("-------获取指定的空参构造------");Constructor con4 = clazz.getConstructor();System.out.println(con4);// 2.5 返回所有构造方法对象的数组System.out.println("-------返回单个构造方法对象------");Constructor con5 = clazz.getDeclaredConstructor(String.class,int.class);System.out.println(con5);System.out.println("-----获取构造方法的权限修饰符(返回整数)--------");// 返回的是整数,public 1,private 2,protected 4,abstract 1024int modifiers = con5.getModifiers();System.out.println(modifiers);System.out.println("-----获取构造方法的参数--------");Parameter[] parameters = con5.getParameters();for (Parameter parameter : parameters) {System.out.println(parameter);}System.out.println("--------创建对象-----------");//暴力反射:表示临时取消权限校验con5.setAccessible(true); // 如果这个权限修饰符是私有的,就要取消权限校验Student stu = (Student) con5.newInstance("张三", 23);System.out.println(stu);}
}
1.6 获取构造方法并创建对象
涉及到的方法:newInstance
代码示例:
public class ReflectDemo02 {public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {//需求1:获取空参,并创建对象(所创建的对象的属性是默认值)//1.获取整体的字节码文件对象Class clazz = Class.forName("com.ya.reflect.demo02.Student");//2.获取空参的构造方法Constructor con = clazz.getConstructor();//3.利用空参构造方法创建对象Student stu = (Student) con.newInstance();System.out.println(stu);System.out.println("----------------------------------");//需求2:获取带参构造,并创建对象//1.获取整体的字节码文件对象Class clazz2 = Class.forName("com.ya.reflect.demo02.Student");//2.获取有参构造方法Constructor con2 = clazz2.getDeclaredConstructor(String.class, int.class);//3.临时修改构造方法的访问权限(暴力反射)con2.setAccessible(true);//4.直接创建对象Student stu2 = (Student) con2.newInstance("zhangsan", 23);System.out.println(stu2);}
}
1.7 获取成员变量并获取值和修改值
获取成员变量规则:
- get表示获取
- Declared表示私有
- 最后的s表示所有,复数形式
- 如果当前获取到的是私有的,必须要临时修改访问权限,否则无法使用
方法名 | 说明 |
---|---|
Field[] getFields() | 返回所有成员变量对象的数组(只能拿public的) |
Field[] getDeclaredFields() | 返回所有成员变量对象的数组,存在就能拿到 |
Field getField(String name) | 返回单个成员变量对象(只能拿public的) |
Field getDeclaredField(String name) | 返回单个成员变量对象,存在就能拿到 |
获取值和修改值:
方法 | 说明 |
---|---|
void set(Object obj, Object value) | 赋值 |
Object get(Object obj) | 获取值 |
代码示例:
public class Student {private String name;private int age;public String gender;public Student() {}public Student(String name, int age, String gender) {this.name = name;this.age = age;this.gender = gender;}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 String getGender() {return gender;}public void setGender(String gender) {this.gender = gender;}public String toString() {return "Student{name = " + name + ", age = " + age + ", gender = " + gender + "}";}
}
public class ReflectDemo {public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {//1.获取class字节码文件的对象Class clazz = Class.forName("com.ya.reflect.demo03.Student");//2.获取所有的成员变量System.out.println("-----获取所有的成员变量----");Field[] fields = clazz.getDeclaredFields();for (Field field : fields) {System.out.println(field);}//3.获取单个的成员变量System.out.println("-----获取单个的成员变量----");Field fieldName = clazz.getDeclaredField("name");System.out.println(fieldName);System.out.println("-----获取成员变量的数据类型----");Class<?> type = fieldName.getType();System.out.println(type);System.out.println("-----获取成员变量记录的值----");Student student = new Student("zhangsan", 23, "男");fieldName.setAccessible(true);String value = (String) fieldName.get(student);System.out.println(value);System.out.println("-----修改对象里面记录的值---");fieldName.set(student,"lisi");System.out.println(student);}
}
1.8 获取成员方法
规则:
- get表示获取
- Declared表示私有
- 最后的s表示所有,复数形式
- 如果当前获取到的是私有的,必须要临时修改访问权限,否则无法使用
方法名 | 说明 |
---|---|
Method[] getMethods() | 返回所有成员方法对象的数组(只能拿public的,包含父类中所有的公共方法) |
Method[] getDeclaredMethods() | 返回所有成员方法对象的数组,存在就能拿到,不能获取父类的 |
Method getMethod(String name, Class<?>… parameterTypes) | 返回单个成员方法对象(只能拿public的) |
Method getDeclaredMethod(String name, Class<?>… parameterTypes) | 返回单个成员方法对象,存在就能拿到 |
获取方法的修饰符
获取方法的名字
获取方法的形参
获取方法的返回值
获取方法的抛出的异常
代码示例:
public class Student {private String name;private int age;public Student() {}public Student(String name, int age) {this.name = name;this.age = age;}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 sleep(){System.out.println("睡觉");}private String eat(String something) throws IOException,NullPointerException,ClassCastException {System.out.println("在吃" + something);return "奥利给";}private void eat(String something,int a) {System.out.println("在吃" + something);}public String toString() {return "Student{name = " + name + ", age = " + age + "}";}
}
public class ReflectDemo {public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {//1. 获取class字节码文件对象Class clazz = Class.forName("com.ya.reflect.demo04.Student");//2. 获取里面所有的方法对象(包含父类中所有的公共方法)System.out.println("-------获取里面所有的方法对象(包含父类中所有的公共方法)-----");Method[] methods = clazz.getMethods();for (Method method : methods) {System.out.println(method);}// 3. 获取里面所有的方法对象(不能获取父类的,但是可以获取本类中私有的方法)System.out.println("--获取里面所有的方法对象(不能获取父类的,但是可以获取本类中私有的方法)--");Method[] declaredMethods = clazz.getDeclaredMethods();for (Method declaredMethod : declaredMethods) {System.out.println(declaredMethod);}// 4.获取指定的单一方法System.out.println("-------获取指定的单一方法-----");Method eatMethod = clazz.getDeclaredMethod("eat", String.class);System.out.println(eatMethod);// 5.获取方法的修饰符(返回整数)System.out.println("-------获取方法的修饰符(返回整数)-----");int modifiers = eatMethod.getModifiers();System.out.println(modifiers);// 6.获取方法的名字System.out.println("-------获取方法的名字-----");String eatMethodName = eatMethod.getName();System.out.println(eatMethodName);// 7.获取方法的形参System.out.println("-------获取方法的形参-----");Parameter[] parameters = eatMethod.getParameters();for (Parameter parameter : parameters) {System.out.println(parameter);}//8.获取方法的抛出的异常System.out.println("-------获取方法的抛出的异常-----");Class[] exceptionTypes = eatMethod.getExceptionTypes();for (Class exceptionType : exceptionTypes) {System.out.println(exceptionType);}}
}
1.9 获取成员方法并运行
Object invoke(Object obj, Object… args) :运行方法
- 参数一:用obj对象调用该方法
- 参数二:调用方法的传递的参数(如果没有就不写)
- 返回值:方法的返回值(如果没有就不写)
代码示例:
public class ReflectDemo {public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {//1. 获取class字节码文件对象Class clazz = Class.forName("com.ya.reflect.demo04.Student");//2. 获取指定的单一方法Method eatMethod = clazz.getDeclaredMethod("eat", String.class);// 3. 方法运行System.out.println("-------方法运行-----");Student student = new Student();eatMethod.setAccessible(true);String result = (String) eatMethod.invoke(student, "汉堡包");System.out.println(result);}
}
1.10 反射练习
1.10.1 泛型擦除
集合中的泛型只在java文件中存在,当编译成class文件之后,就没有泛型了。
代码示例:
public class Test1 {public static void main(String[] args) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {//1.创建集合对象ArrayList<Integer> list = new ArrayList<>();list.add(123);
// list.add("aaa");//2.利用反射运行add方法去添加字符串//因为反射使用的是class字节码文件//获取class对象Class clazz = list.getClass();//获取add方法对象Method method = clazz.getMethod("add", Object.class);//运行方法method.invoke(list,"aaa");//打印集合System.out.println(list);}
}
1.10.2 修改字符串的内容
在这个练习中,需要掌握的是字符串不能修改的真正原因。
字符串,在底层是一个byte类型的字节数组,名字叫做value
private final byte[] value;
真正不能被修改的原因:final和private
final修饰value表示value记录的地址值不能修改。
private修饰value而且没有对外提供getvalue和setvalue的方法。所以,在外界不能获取或修改value记录的地址值。
如果要强行修改可以用反射:
代码示例:
public class Test2 {public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {String s = "abc";String ss = "abc";// private final byte[] value= {97,98,99};/*** 没有对外提供getvalue和setvalue的方法,不能修改value记录的地址值,* 如果我们利用反射获取了value的地址值,也是可以修改的,* final修饰的value,真正不可变的value数组的地址值,里面的内容利用反射还是可以修改的,* 但是这种操作非常危险,JDK高版本已经屏蔽了这种操作,低版本还是可以的*///1.获取class对象Class clazz = s.getClass();//2.获取value成员变量(private)Field field = clazz.getDeclaredField("value");//临时修改权限field.setAccessible(true);//3.获取value记录的地址值byte[] bytes = (byte[]) field.get(s);bytes[0] = 100; // 对应的字符编码为 'd'System.out.println(s); //输出dbcSystem.out.println(ss); //输出dbc}
}
1.10.3 利用反射保存对象中的信息
利用反射保存对象中的信息:对于任意一个对象,都可以把对象所有的字段名和值,保存到文件中去。
需求:创建对象,并将对象信息保存到txt文件中。
Student
public class Student {private String name;private int age;private char gender;private double height;private String hobby;public Student() {}public Student(String name, int age, char gender, double height, String hobby) {this.name = name;this.age = age;this.gender = gender;this.height = height;this.hobby = hobby;}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 char getGender() {return gender;}public void setGender(char gender) {this.gender = gender;}public double getHeight() {return height;}public void setHeight(double height) {this.height = height;}public String getHobby() {return hobby;}public void setHobby(String hobby) {this.hobby = hobby;}public String toString() {return "Student{name = " + name + ", age = " + age + ", gender = " + gender + ", height = " + height + ", hobby = " + hobby + "}";}
}
Teacher
public class Teacher {private String name;private double salary;public Teacher() {}public Teacher(String name, double salary) {this.name = name;this.salary = salary;}public String getName() {return name;}public void setName(String name) {this.name = name;}public double getSalary() {return salary;}public void setSalary(double salary) {this.salary = salary;}public String toString() {return "Teacher{name = " + name + ", salary = " + salary + "}";}
}
public class ReflectDemo {public static void main(String[] args) throws IOException, IllegalAccessException {Student s = new Student("张三",23,'女',167.5,"睡觉");Teacher t = new Teacher("李四",10000);//saveObject(s);saveObject(t);}//把对象里面所有的成员变量名和值保存到本地文件中public static void saveObject(Object obj) throws IllegalAccessException, IOException {//1.获取字节码文件的对象Class clazz = obj.getClass();//2. 创建IO流BufferedWriter bw = new BufferedWriter(new FileWriter("my-reflect-dynamicproxy/src/a.txt"));//3. 获取所有的成员变量Field[] fields = clazz.getDeclaredFields();for (Field field : fields) {field.setAccessible(true);//获取成员变量的名字String name = field.getName();//获取成员变量的值Object value = field.get(obj);//写出数据bw.write(name + "=" + value);bw.newLine();}bw.close();}
}
1.10.4反射结合配置文件
需求:利用反射根据文件中的不同类名和方法名,创建不同的对象并调用方法。
分析:
- 通过Properties加载配置文件
- 得到类名和方法名
- 通过类名反射得到Class对象
- 通过Class对象创建一个对象
- 通过Class对象得到方法
- 调用方法
代码示例:
prop.properties
classname=com.ya.reflect.demo06.Teacher
method=teach#classname=com.ya.reflect.demo06.Student
#method=study
public class Student {private String name;private int age;public Student() {}public Student(String name, int age) {this.name = name;this.age = age;}public void study(){System.out.println("学生在学习!");}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 String toString() {return "Student{name = " + name + ", age = " + age + "}";}
}
public class Teacher {private String name;private double salary;public Teacher() {}public Teacher(String name, double salary) {this.name = name;this.salary = salary;}public void teach(){System.out.println("老师在教书!");}public String getName() {return name;}public void setName(String name) {this.name = name;}public double getSalary() {return salary;}public void setSalary(double salary) {this.salary = salary;}public String toString() {return "Teacher{name = " + name + ", salary = " + salary + "}";}
}
public class ReflectDemo {public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {//1.读取配置文件中的信息Properties prop = new Properties();FileInputStream fis = new FileInputStream("my-reflect-dynamicproxy/src/prop.properties");prop.load(fis);fis.close();System.out.println(prop);//2.获取全类名和方法名String className = (String) prop.get("classname");String methodName = (String) prop.get("method");System.out.println(className);System.out.println(methodName);//3.利用反射创建对象并运行方法Class clazz = Class.forName(className);//获取构造方法Constructor con = clazz.getDeclaredConstructor();Object o = con.newInstance();System.out.println(o);//获取成员方法并运行Method method = clazz.getDeclaredMethod(methodName);method.setAccessible(true);method.invoke(o);}
}
2. 动态代理
2.1 动态代理好处
无侵入式的给方法增强功能。调用者(BitStar)–>代理(ProxyUtil)–>对象(Star)
2.2 动态代理三要素
- 接口:代理对象,被代理类和代理类都需要实现这个接口(本例是Star)
- 代理类:通过Proxy类动态生成的代理类(本例是ProxyUtil)
- 被代理类:代理类实例,它会代替被代理对象处理方法调用(本例是BigStar)
注:代理可以增强或者拦截的方法都在接口中,接口需要写在newProxyInstance的第二个参数里。
2.3 动态代理简单实现
需求:外面的人想要大明星唱一首歌
- 获取代理的对象:代理对象 = ProxyUtil.createProxy(大明星的对象);
- 再调用代理的唱歌方法:代理对象.唱歌的方法(“只因你太美”);
(1)接口:代理对象,定义被代理的方法
public interface Star {//可以把所有想要被代理的方法定义在接口当中//唱歌public abstract String sing(String name);//跳舞public abstract void dance();
}
(2)被代理类:需要使用代理的实体类,要实现代理对象接口
public class BigStar implements Star{private String name;public BigStar() {}public BigStar(String name) {this.name = name;}//唱歌@Overridepublic String sing(String name){System.out.println(this.name + "正在唱" + name);return "谢谢";}//跳舞@Overridepublic void dance(){System.out.println(this.name + "正在跳舞");}/*** 获取* @return name*/public String getName() {return name;}/*** 设置* @param name*/public void setName(String name) {this.name = name;}public String toString() {return "BigStar{name = " + name + "}";}
}
(3)代理类:创建代理
java.lang.reflect.Proxy类:提供了为对象产生代理对象的方法
// 创建代理
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
参数一:用于指定用哪个类加载器,去加载生成的代理类
参数二:指定接口,这些接口用于指定生成的代理有哪些方法
参数三:用来指定生成的代理对象要干什么事情
public class ProxyUtil {/*** 形参:被代理的明星对象* 返回值:给明星创建的代理*/public static Star createProxy(BigStar bigStar){// 创建代理// 这个代码没有显示实现Star接口,但通过JDK动态代理机制隐式地为生成的代理类添加了Star接口的实现,从而可以看作实现了Star接口Star star = (Star) Proxy.newProxyInstance(ProxyUtil.class.getClassLoader(),//参数一:用于指定用哪个类加载器,去加载生成的代理类new Class[]{Star.class},//参数二:指定接口,这些接口用于指定生成的代理有哪些方法new InvocationHandler() { //参数三:用来指定生成的代理对象要干什么事情@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {/*** 参数一:代理的对象,即star变量引用的对象* 参数二:要运行的方法,sing,dance* 参数三:调用sing方法时,传递的实参*/if("sing".equals(method.getName())){System.out.println("准备话筒,收钱");}else if("dance".equals(method.getName())){System.out.println("准备场地,收钱");}//去找大明星开始唱歌或者跳舞//代码的表现形式:调用大明星里面唱歌或者跳舞的方法return method.invoke(bigStar,args);}});return star;}
}
(4)代理测试
public class Test {public static void main(String[] args) {//1. 获取代理的对象BigStar bigStar = new BigStar("鸡哥");Star proxy = ProxyUtil.createProxy(bigStar);//2. 调用唱歌的方法String result = proxy.sing("只因你太美");System.out.println(result);}
}
2.4 动态代理扩展
动态代理,还可以拦截方法。比如:在这个故事中,经纪人作为代理,如果别人让邀请大明星去唱歌,打篮球,经纪人就增强功能。但是如果别人让大明星去扫厕所,经纪人就要拦截,不会去调用大明星的方法。
public class ProxyUtil {public static Star createProxy(BigStar bigStar){public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)Star star = (Star) Proxy.newProxyInstance(ProxyUtil.class.getClassLoader(),new Class[]{Star.class},new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {if("cleanWC".equals(method.getName())){System.out.println("拦截,不调用大明星的方法");return null;}//如果是其他方法,正常执行return method.invoke(bigStar,args);}});return star;}
}
2.5 动态代理练习
需求:对add方法进行增强,对remove方法进行拦截,对其他方法不拦截也不增强。
代码实现:
public class DynamicProxyTest {public static void main(String[] args) {//1.创建真正干活的人ArrayList<String> list = new ArrayList<>();/*** 2.创建代理对象* 参数一:类加载器。当前类名.class.getClassLoader()* 把当前的类,加载到内存中了,把后面的代理类,也加载到内存* 参数二:是一个数组,在数组里面写接口的字节码文件对象。* 如果写了List,那么表示代理,可以代理List接口里面所有的方法,对这些方法可以增强或者拦截* 但是,一定要写ArrayList真实实现的接口* 假设在第二个参数中,写了MyInter接口,那么是错误的。* 因为ArrayList并没有实现这个接口,那么就无法对这个接口里面的方法,进行增强或拦截* 参数三:用来创建代理对象的匿名内部类*/List proxyList = (List) Proxy.newProxyInstance(//参数一:类加载器DynamicProxyTest.class.getClassLoader(),//参数二:是一个数组,表示代理对象能代理的方法范围new Class[]{List.class},//参数三:本质就是代理对象new InvocationHandler() {/*** invoke方法参数的意义* 参数一:表示代理对象,一般不用(了解)* 参数二:就是方法名,我们可以对方法名进行判断,是增强还是拦截* 参数三:就是下面第三步调用方法时,传递的参数。** 举例1:* list.add("阿玮好帅");* 此时参数二就是add这个方法名* 此时参数三 args[0] 就是 阿玮好帅** 举例2:* list.set(1, "aaa");* 此时参数二就是set这个方法名* 此时参数三 args[0] 就是 1 args[1]"aaa"*/@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//对add方法做一个增强,统计耗时时间if (method.getName().equals("add")) {long start = System.currentTimeMillis();//调用集合的方法,真正的添加数据method.invoke(list, args);long end = System.currentTimeMillis();System.out.println("耗时时间:" + (end - start));//需要进行返回,返回值要跟真正增强或者拦截的方法保持一致return true;} else if (method.getName().equals("remove") && args[0] instanceof Integer) {System.out.println("拦截了按照索引删除的方法");return null;} else if (method.getName().equals("remove")) {System.out.println("拦截了按照对象删除的方法");return false;} else {//如果当前调用的是其他方法,我们既不增强,也不拦截method.invoke(list, args);return null;}}});/*** 3.调用方法* 如果调用者是list,就好比绕过了第二步的代码,直接添加元素* 如果调用者是代理对象,此时代理才能帮我们增强或者拦截* 每次调用方法的时候,都不会直接操作集合* 而是先调用代理里面的invoke,在invoke方法中进行判断,可以增强或者拦截*/proxyList.add("aaa");proxyList.add("bbb");proxyList.add("ccc");proxyList.add("ddd");proxyList.remove(0);proxyList.remove("aaa");//打印集合System.out.println(list);}
}