Java高級-反射&動態(tài)代理詳解
1. 反射
1.1 反射的概述
專業(yè)的解釋(了解一下):
- 是在運行狀態(tài)中,對于任意一個類,都能夠知道這個類的所有屬性和方法;
- 對于任意一個對象,都能夠調用它的任意屬性和方法;
- 這種動態(tài)獲取信息以及動態(tài)調用對象方法的功能稱為Java語言的反射機制。
通俗的理解:(掌握)
利用反射創(chuàng)建的對象可以無視修飾符調用類里面的內容
可以跟配置文件結合起來使用,把要創(chuàng)建的對象信息和方法寫在配置文件中。
- 讀取到什么類,就創(chuàng)建什么類的對象
- 讀取到什么方法,就調用什么方法
- 此時當需求變更的時候不需要修改代碼,只要修改配置文件即可。
1.2 學習反射到底學什么?
反射都是從class字節(jié)碼文件中獲取的內容。(前提)
- 如何獲取class字節(jié)碼文件的對象
- 利用反射如何獲取構造方法(創(chuàng)建對象)
- 利用反射如何獲取成員變量(賦值,獲取值)
- 利用反射如何獲取成員方法(運行)
1.3 獲取字節(jié)碼文件對象的三種方式
- Class這個類里面的靜態(tài)方法forName(“全類名”)(最常用)
- 通過class屬性獲取
- 通過對象獲取字節(jié)碼文件對象
代碼示例:
//1.Class這個類里面的靜態(tài)方法forName
//Class.forName("類的全類名"): 全類名 = 包名 + 類名
Class clazz1 = Class.forName("com.itheima.reflectdemo.Student");
//源代碼階段獲取 --- 先把Student加載到內存中,再獲取字節(jié)碼文件的對象
//clazz 就表示Student這個類的字節(jié)碼文件對象。
//就是當Student.class這個文件加載到內存之后,產生的字節(jié)碼文件對象
?
?
//2.通過class屬性獲取
//類名.class
Class clazz2 = Student.class;
?
//因為class文件在硬盤中是唯一的,所以,當這個文件加載到內存之后產生的對象也是唯一的
System.out.println(clazz1 == clazz2);//true
?
?
//3.通過Student對象獲取字節(jié)碼文件對象
Student s = new Student();
Class clazz3 = s.getClass();
System.out.println(clazz1 == clazz2);//true
System.out.println(clazz2 == clazz3);//true1.4 字節(jié)碼文件和字節(jié)碼文件對象
- java文件:就是我們自己編寫的java代碼。
- 字節(jié)碼文件:就是通過java文件編譯之后的class文件(是在硬盤上真實存在的,用眼睛能看到的)
- 字節(jié)碼文件對象:當class文件加載到內存之后,虛擬機自動創(chuàng)建出來的對象。
- 這個對象里面至少包含了:構造方法,成員變量,成員方法。
- 而我們的反射獲取的是什么?字節(jié)碼文件對象,這個對象在內存中是唯一的。
1.5 獲取構造方法
規(guī)則:
- get表示獲取
- Declared表示私有
- 最后的s表示所有,復數(shù)形式
- 如果當前獲取到的是私有的,必須要臨時修改訪問權限,否則無法使用
| 方法名 | 說明 |
|---|---|
| Constructor<?>[] getConstructors() | 獲得所有的構造(只能public修飾) |
| Constructor<?>[] getDeclaredConstructors() | 獲得所有的構造(包含private修飾) |
| Constructor<T> getConstructor(Class<?>... parameterTypes) | 獲取指定構造(只能public修飾) |
| Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) | 獲取指定構造(包含private修飾) |
代碼示例:
public class ReflectDemo2 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
//1.獲得整體(class字節(jié)碼文件對象)
Class clazz = Class.forName("com.itheima.reflectdemo.Student");
?
?
//2.獲取構造方法對象
//獲取所有構造方法(public)
Constructor[] constructors1 = clazz.getConstructors();
for (Constructor constructor : constructors1) {
System.out.println(constructor);
}
?
System.out.println("=======================");
?
//獲取所有構造(帶私有的)
Constructor[] constructors2 = clazz.getDeclaredConstructors();
?
for (Constructor constructor : constructors2) {
System.out.println(constructor);
}
System.out.println("=======================");
?
//獲取指定的空參構造
Constructor con1 = clazz.getConstructor();
System.out.println(con1);
?
Constructor con2 = clazz.getConstructor(String.class,int.class);
System.out.println(con2);
?
System.out.println("=======================");
//獲取指定的構造(所有構造都可以獲取到,包括public包括private)
Constructor con3 = clazz.getDeclaredConstructor();
System.out.println(con3);
//了解 System.out.println(con3 == con1);
//每一次獲取構造方法對象的時候,都會新new一個。
?
Constructor con4 = clazz.getDeclaredConstructor(String.class);
System.out.println(con4);
}
}1.6 獲取構造方法并創(chuàng)建對象
涉及到的方法:newInstance
代碼示例:
//首先要有一個javabean類
public class Student {
private String name;
?
private int age;
?
?
public Student() {
?
}
?
public Student(String name) {
this.name = name;
}
?
private Student(String name, int age) {
this.name = name;
this.age = age;
}
?
?
/**
* 獲取
* @return name
*/
public String getName() {
return name;
}
?
/**
* 設置
* @param name
*/
public void setName(String name) {
this.name = name;
}
?
/**
* 獲取
* @return age
*/
public int getAge() {
return age;
}
?
/**
* 設置
* @param age
*/
public void setAge(int age) {
this.age = age;
}
?
public String toString() {
return "Student{name = " + name + ", age = " + age + "}";
}
}
?
?
?
//測試類中的代碼:
//需求1:
//獲取空參,并創(chuàng)建對象
?
//1.獲取整體的字節(jié)碼文件對象
Class clazz = Class.forName("com.itheima.a02reflectdemo1.Student");
//2.獲取空參的構造方法
Constructor con = clazz.getConstructor();
//3.利用空參構造方法創(chuàng)建對象
Student stu = (Student) con.newInstance();
System.out.println(stu);
?
?
System.out.println("=============================================");
?
?
//測試類中的代碼:
//需求2:
//獲取帶參構造,并創(chuàng)建對象
//1.獲取整體的字節(jié)碼文件對象
Class clazz = Class.forName("com.itheima.a02reflectdemo1.Student");
//2.獲取有參構造方法
Constructor con = clazz.getDeclaredConstructor(String.class, int.class);
//3.臨時修改構造方法的訪問權限(暴力反射)
con.setAccessible(true);
//4.直接創(chuàng)建對象
Student stu = (Student) con.newInstance("zhangsan", 23);
System.out.println(stu);1.7 獲取成員變量
規(guī)則:
- get表示獲取
- Declared表示私有
- 最后的s表示所有,復數(shù)形式
- 如果當前獲取到的是私有的,必須要臨時修改訪問權限,否則無法使用
方法名:
| 方法名 | 說明 |
|---|---|
| Field[] getFields() | 返回所有成員變量對象的數(shù)組(只能拿public的) |
| Field[] getDeclaredFields() | 返回所有成員變量對象的數(shù)組,存在就能拿到 |
| Field getField(String name) | 返回單個成員變量對象(只能拿public的) |
| Field getDeclaredField(String name) | 返回單個成員變量對象,存在就能拿到 |
代碼示例:
public class ReflectDemo4 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
//獲取成員變量對象
?
//1.獲取class對象
Class clazz = Class.forName("com.itheima.reflectdemo.Student");
?
//2.獲取成員變量的對象(Field對象)只能獲取public修飾的
Field[] fields1 = clazz.getFields();
for (Field field : fields1) {
System.out.println(field);
}
?
System.out.println("===============================");
?
//獲取成員變量的對象(public + private)
Field[] fields2 = clazz.getDeclaredFields();
for (Field field : fields2) {
System.out.println(field);
}
?
System.out.println("===============================");
//獲得單個成員變量對象
//如果獲取的屬性是不存在的,那么會報異常
//Field field3 = clazz.getField("aaa");
//System.out.println(field3);//NoSuchFieldException
?
Field field4 = clazz.getField("gender");
System.out.println(field4);
?
System.out.println("===============================");
//獲取單個成員變量(私有)
Field field5 = clazz.getDeclaredField("name");
System.out.println(field5);
?
}
}
?
?
?
public class Student {
private String name;
?
private int age;
?
public String gender;
?
public String address;
?
?
public Student() {
}
?
public Student(String name, int age, String address) {
this.name = name;
this.age = age;
this.address = address;
}
?
?
public Student(String name, int age, String gender, String address) {
this.name = name;
this.age = age;
this.gender = gender;
this.address = address;
}
?
/**
* 獲取
* @return name
*/
public String getName() {
return name;
}
?
/**
* 設置
* @param name
*/
public void setName(String name) {
this.name = name;
}
?
/**
* 獲取
* @return age
*/
public int getAge() {
return age;
}
?
/**
* 設置
* @param age
*/
public void setAge(int age) {
this.age = age;
}
?
/**
* 獲取
* @return gender
*/
public String getGender() {
return gender;
}
?
/**
* 設置
* @param gender
*/
public void setGender(String gender) {
this.gender = gender;
}
?
/**
* 獲取
* @return address
*/
public String getAddress() {
return address;
}
?
/**
* 設置
* @param address
*/
public void setAddress(String address) {
this.address = address;
}
?
public String toString() {
return "Student{name = " + name + ", age = " + age + ", gender = " + gender + ", address = " + address + "}";
}
}1.8 獲取成員變量并獲取值和修改值
| 方法 | 說明 |
|---|---|
| void set(Object obj, Object value) | 賦值 |
| Object get(Object obj) | 獲取值 |
代碼示例:
public class ReflectDemo5 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
Student s = new Student("zhangsan",23,"廣州");
Student ss = new Student("lisi",24,"北京");
?
//需求:
//利用反射獲取成員變量并獲取值和修改值
?
//1.獲取class對象
Class clazz = Class.forName("com.itheima.reflectdemo.Student");
?
//2.獲取name成員變量
//field就表示name這個屬性的對象
Field field = clazz.getDeclaredField("name");
//臨時修飾他的訪問權限
field.setAccessible(true);
?
//3.設置(修改)name的值
//參數(shù)一:表示要修改哪個對象的name?
//參數(shù)二:表示要修改為多少?
field.set(s,"wangwu");
?
//3.獲取name的值
//表示我要獲取這個對象的name的值
String result = (String)field.get(s);
?
//4.打印結果
System.out.println(result);
?
System.out.println(s);
System.out.println(ss);
?
}
}
?
?
public class Student {
private String name;
private int age;
public String gender;
public String address;
?
?
public Student() {
}
?
public Student(String name, int age, String address) {
this.name = name;
this.age = age;
this.address = address;
}
?
?
public Student(String name, int age, String gender, String address) {
this.name = name;
this.age = age;
this.gender = gender;
this.address = address;
}
?
/**
* 獲取
* @return name
*/
public String getName() {
return name;
}
?
/**
* 設置
* @param name
*/
public void setName(String name) {
this.name = name;
}
?
/**
* 獲取
* @return age
*/
public int getAge() {
return age;
}
?
/**
* 設置
* @param age
*/
public void setAge(int age) {
this.age = age;
}
?
/**
* 獲取
* @return gender
*/
public String getGender() {
return gender;
}
?
/**
* 設置
* @param gender
*/
public void setGender(String gender) {
this.gender = gender;
}
?
/**
* 獲取
* @return address
*/
public String getAddress() {
return address;
}
?
/**
* 設置
* @param address
*/
public void setAddress(String address) {
this.address = address;
}
?
public String toString() {
return "Student{name = " + name + ", age = " + age + ", gender = " + gender + ", address = " + address + "}";
}
}1.9 獲取成員方法
規(guī)則:
- get表示獲取
- Declared表示私有
- 最后的s表示所有,復數(shù)形式
- 如果當前獲取到的是私有的,必須要臨時修改訪問權限,否則無法使用
| 方法名 | 說明 |
|---|---|
| Method[] getMethods() | 返回所有成員方法對象的數(shù)組(只能拿public的) |
| Method[] getDeclaredMethods() | 返回所有成員方法對象的數(shù)組,存在就能拿到 |
| Method getMethod(String name, Class<?>... parameterTypes) | 返回單個成員方法對象(只能拿public的) |
| Method getDeclaredMethod(String name, Class<?>... parameterTypes) | 返回單個成員方法對象,存在就能拿到 |
代碼示例:
public class ReflectDemo6 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
//1.獲取class對象
Class<?> clazz = Class.forName("com.itheima.reflectdemo.Student");
?
?
//2.獲取方法
//getMethods可以獲取父類中public修飾的方法
Method[] methods1 = clazz.getMethods();
for (Method method : methods1) {
System.out.println(method);
}
?
System.out.println("===========================");
//獲取所有的方法(包含私有)
//但是只能獲取自己類中的方法
Method[] methods2 = clazz.getDeclaredMethods();
for (Method method : methods2) {
System.out.println(method);
}
?
System.out.println("===========================");
//獲取指定的方法(空參)
Method method3 = clazz.getMethod("sleep");
System.out.println(method3);
?
Method method4 = clazz.getMethod("eat",String.class);
System.out.println(method4);
?
//獲取指定的私有方法
Method method5 = clazz.getDeclaredMethod("playGame");
System.out.println(method5);
}
}1.10 獲取成員方法并運行
方法
- Object invoke(Object obj, Object... args) :運行方法
- 參數(shù)一:用obj對象調用該方法
- 參數(shù)二:調用方法的傳遞的參數(shù)(如果沒有就不寫)
- 返回值:方法的返回值(如果沒有就不寫)
代碼示例:
package com.itheima.a02reflectdemo1;
?
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
?
public class ReflectDemo6 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
//1.獲取字節(jié)碼文件對象
Class clazz = Class.forName("com.itheima.a02reflectdemo1.Student");
//2.獲取一個對象
//需要用這個對象去調用方法
Student s = new Student();
//3.獲取一個指定的方法
//參數(shù)一:方法名
//參數(shù)二:參數(shù)列表,如果沒有可以不寫
Method eatMethod = clazz.getMethod("eat",String.class);
//運行
//參數(shù)一:表示方法的調用對象
//參數(shù)二:方法在運行時需要的實際參數(shù)
//注意點:如果方法有返回值,那么需要接收invoke的結果
//如果方法沒有返回值,則不需要接收
String result = (String) eatMethod.invoke(s, "重慶小面");
System.out.println(result);
?
}
}
?
?
?
public class Student {
private String name;
private int age;
public String gender;
public String address;
?
?
public Student() {
?
}
?
public Student(String name) {
this.name = name;
}
?
private Student(String name, int age) {
this.name = name;
this.age = age;
}
?
/**
* 獲取
* @return name
*/
public String getName() {
return name;
}
?
/**
* 設置
* @param name
*/
public void setName(String name) {
this.name = name;
}
?
/**
* 獲取
* @return age
*/
public int getAge() {
return age;
}
?
/**
* 設置
* @param age
*/
public void setAge(int age) {
this.age = age;
}
?
public String toString() {
return "Student{name = " + name + ", age = " + age + "}";
}
?
private void study(){
System.out.println("學生在學習");
}
?
private void sleep(){
System.out.println("學生在睡覺");
}
?
public String eat(String something){
System.out.println("學生在吃" + something);
return "學生已經吃完了,非常happy";
}
}面試題:
你覺得反射好不好?好,有兩個方向
- 第一個方向:無視修飾符訪問類中的內容。但是這種操作在開發(fā)中一般不用,都是框架底層來用的。
- 第二個方向:反射可以跟配置文件結合起來使用,動態(tài)的創(chuàng)建對象,動態(tài)的調用方法。
1.11 練習泛型擦除(掌握概念,了解代碼)
理解:(掌握)
集合中的泛型只在java文件中存在,當編譯成class文件之后,就沒有泛型了。
代碼示例:(了解)
package com.itheima.reflectdemo;
?
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
?
public class ReflectDemo8 {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
//1.創(chuàng)建集合對象
ArrayList<Integer> list = new ArrayList<>();
list.add(123);
// list.add("aaa");
?
//2.利用反射運行add方法去添加字符串
//因為反射使用的是class字節(jié)碼文件
?
//獲取class對象
Class clazz = list.getClass();
?
//獲取add方法對象
Method method = clazz.getMethod("add", Object.class);
?
//運行方法
method.invoke(list,"aaa");
?
//打印集合
System.out.println(list);
}
}1.12 練習:修改字符串的內容(掌握概念,了解代碼)
在這個練習中,我需要你掌握的是字符串不能修改的真正原因。
字符串,在底層是一個byte類型的字節(jié)數(shù)組,名字叫做value
private final byte[] value;
真正不能被修改的原因:final和private
- final修飾value表示value記錄的地址值不能修改。
- private修飾value而且沒有對外提供getvalue和setvalue的方法。所以,在外界不能獲取或修改value記錄的地址值。
如果要強行修改可以用反射:
代碼示例:(了解)
String s = "abc";
String ss = "abc";
// private final byte[] value= {97,98,99};
// 沒有對外提供getvalue和setvalue的方法,不能修改value記錄的地址值
// 如果我們利用反射獲取了value的地址值。
// 也是可以修改的,final修飾的value
// 真正不可變的value數(shù)組的地址值,里面的內容利用反射還是可以修改的,比較危險
?
//1.獲取class對象
Class clazz = s.getClass();
?
//2.獲取value成員變量(private)
Field field = clazz.getDeclaredField("value");
//但是這種操作非常危險
//JDK高版本已經屏蔽了這種操作,低版本還是可以的
//臨時修改權限
field.setAccessible(true);
?
//3.獲取value記錄的地址值
byte[] bytes = (byte[]) field.get(s);
bytes[0] = 100;
?
System.out.println(s);//dbc
System.out.println(ss);//dbc1.13 練習,反射和配置文件結合動態(tài)獲取的練習(重點)
需求: 利用反射根據(jù)文件中的不同類名和方法名,創(chuàng)建不同的對象并調用方法。
分析:
- ①通過Properties加載配置文件
- ②得到類名和方法名
- ③通過類名反射得到Class對象
- ④通過Class對象創(chuàng)建一個對象
- ⑤通過Class對象得到方法
- ⑥調用方法
代碼示例:
public class ReflectDemo9 {
public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
//1.讀取配置文件的信息
Properties prop = new Properties();
FileInputStream fis = new FileInputStream("day14-code\\prop.properties");
prop.load(fis);
fis.close();
System.out.println(prop);
?
String classname = prop.get("classname") + "";
String methodname = prop.get("methodname") + "";
?
//2.獲取字節(jié)碼文件對象
Class clazz = Class.forName(classname);
?
//3.要先創(chuàng)建這個類的對象
Constructor con = clazz.getDeclaredConstructor();
con.setAccessible(true);
Object o = con.newInstance();
System.out.println(o);
?
//4.獲取方法的對象
Method method = clazz.getDeclaredMethod(methodname);
method.setAccessible(true);
?
//5.運行方法
method.invoke(o);
?
?
}
}
?
配置文件中的信息:
classname=com.itheima.a02reflectdemo1.Student
methodname=sleep1.14 利用反射保存對象中的信息(重點)
public class MyReflectDemo {
public static void main(String[] args) throws IllegalAccessException, IOException {
/*
對于任意一個對象,都可以把對象所有的字段名和值,保存到文件中去
*/
Student s = new Student("小A",23,'女',167.5,"睡覺");
Teacher t = new Teacher("播妞",10000);
saveObject(s);
}
?
//把對象里面所有的成員變量名和值保存到本地文件中
public static void saveObject(Object obj) throws IllegalAccessException, IOException {
//1.獲取字節(jié)碼文件的對象
Class clazz = obj.getClass();
//2. 創(chuàng)建IO流
BufferedWriter bw = new BufferedWriter(new FileWriter("myreflect\\a.txt"));
//3. 獲取所有的成員變量
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
//獲取成員變量的名字
String name = field.getName();
//獲取成員變量的值
Object value = field.get(obj);
//寫出數(shù)據(jù)
bw.write(name + "=" + value);
bw.newLine();
}
?
bw.close();
?
}
}
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;
}
?
/**
* 獲取
* @return name
*/
public String getName() {
return name;
}
?
/**
* 設置
* @param name
*/
public void setName(String name) {
this.name = name;
}
?
/**
* 獲取
* @return age
*/
public int getAge() {
return age;
}
?
/**
* 設置
* @param age
*/
public void setAge(int age) {
this.age = age;
}
?
/**
* 獲取
* @return gender
*/
public char getGender() {
return gender;
}
?
/**
* 設置
* @param gender
*/
public void setGender(char gender) {
this.gender = gender;
}
?
/**
* 獲取
* @return height
*/
public double getHeight() {
return height;
}
?
/**
* 設置
* @param height
*/
public void setHeight(double height) {
this.height = height;
}
?
/**
* 獲取
* @return hobby
*/
public String getHobby() {
return hobby;
}
?
/**
* 設置
* @param hobby
*/
public void setHobby(String hobby) {
this.hobby = hobby;
}
?
public String toString() {
return "Student{name = " + name + ", age = " + age + ", gender = " + gender + ", height = " + height + ", hobby = " + hobby + "}";
}
}
public class Teacher {
private String name;
private double salary;
?
public Teacher() {
}
?
public Teacher(String name, double salary) {
this.name = name;
this.salary = salary;
}
?
/**
* 獲取
* @return name
*/
public String getName() {
return name;
}
?
/**
* 設置
* @param name
*/
public void setName(String name) {
this.name = name;
}
?
/**
* 獲取
* @return salary
*/
public double getSalary() {
return salary;
}
?
/**
* 設置
* @param salary
*/
public void setSalary(double salary) {
this.salary = salary;
}
?
public String toString() {
return "Teacher{name = " + name + ", salary = " + salary + "}";
}
}
?2. 動態(tài)代理
2.1 好處
無侵入式的給方法增強功能
2.2 動態(tài)代理三要素
- 1,真正干活的對象
- 2,代理對象
- 3,利用代理調用方法
切記一點:代理可以增強或者攔截的方法都在接口中,接口需要寫在newProxyInstance的第二個參數(shù)里。
2.3 代碼實現(xiàn)
public class Test {
public static void main(String[] args) {
/*
需求:
外面的人想要大明星唱一首歌
1. 獲取代理的對象
代理對象 = ProxyUtil.createProxy(大明星的對象);
2. 再調用代理的唱歌方法
代理對象.唱歌的方法("只因你太美");
*/
//1. 獲取代理的對象
BigStar bigStar = new BigStar("雞哥");
Star proxy = ProxyUtil.createProxy(bigStar);
?
//2. 調用唱歌的方法
String result = proxy.sing("只因你太美");
System.out.println(result);
}
}
/*
*
* 類的作用:
* 創(chuàng)建一個代理
*
* */
public class ProxyUtil {
/*
*
* 方法的作用:
* 給一個明星的對象,創(chuàng)建一個代理
*
* 形參:
* 被代理的明星對象
*
* 返回值:
* 給明星創(chuàng)建的代理
*
*
*
* 需求:
* 外面的人想要大明星唱一首歌
* 1. 獲取代理的對象
* 代理對象 = ProxyUtil.createProxy(大明星的對象);
* 2. 再調用代理的唱歌方法
* 代理對象.唱歌的方法("只因你太美");
* */
public static Star createProxy(BigStar bigStar){
/* java.lang.reflect.Proxy類:提供了為對象產生代理對象的方法:
?
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
參數(shù)一:用于指定用哪個類加載器,去加載生成的代理類
參數(shù)二:指定接口,這些接口用于指定生成的代理長什么,也就是有哪些方法
參數(shù)三:用來指定生成的代理對象要干什么事情*/
Star star = (Star) Proxy.newProxyInstance(
ProxyUtil.class.getClassLoader(),//參數(shù)一:用于指定用哪個類加載器,去加載生成的代理類
new Class[]{Star.class},//參數(shù)二:指定接口,這些接口用于指定生成的代理長什么,也就是有哪些方法
//參數(shù)三:用來指定生成的代理對象要干什么事情
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
/*
* 參數(shù)一:代理的對象
* 參數(shù)二:要運行的方法 sing
* 參數(shù)三:調用sing方法時,傳遞的實參
* */
if("sing".equals(method.getName())){
System.out.println("準備話筒,收錢");
}else if("dance".equals(method.getName())){
System.out.println("準備場地,收錢");
}
//去找大明星開始唱歌或者跳舞
//代碼的表現(xiàn)形式:調用大明星里面唱歌或者跳舞的方法
return method.invoke(bigStar,args);
}
}
);
return star;
}
}
public interface Star {
//我們可以把所有想要被代理的方法定義在接口當中
//唱歌
public abstract String sing(String name);
//跳舞
public abstract void dance();
}
public class BigStar implements Star {
private String name;
?
?
public BigStar() {
}
?
public BigStar(String name) {
this.name = name;
}
?
//唱歌
@Override
public String sing(String name){
System.out.println(this.name + "正在唱" + name);
return "謝謝";
}
?
//跳舞
@Override
public 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 + "}";
}
}
?2.4 額外擴展
動態(tài)代理,還可以攔截方法(重點)
比如:
- 在這個故事中,經濟人作為代理,如果別人讓邀請大明星去唱歌,打籃球,經紀人就增強功能。
- 但是如果別人讓大明星去掃廁所,經紀人就要攔截,不會去調用大明星的方法。
/*
* 類的作用:
* 創(chuàng)建一個代理
* */
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() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if("cleanWC".equals(method.getName())){
System.out.println("攔截,不調用大明星的方法");
return null;
}
//如果是其他方法,正常執(zhí)行
return method.invoke(bigStar,args);
}
}
);
return star;
}
}2.5 動態(tài)代理的練習
對add方法進行增強,對remove方法進行攔截,對其他方法不攔截也不增強
public class MyProxyDemo1 {
public static void main(String[] args) {
//動態(tài)代碼可以增強也可以攔截
//1.創(chuàng)建真正干活的人
ArrayList<String> list = new ArrayList<>();
?
//2.創(chuàng)建代理對象
//參數(shù)一:類加載器。當前類名.class.getClassLoader()
// 找到是誰,把當前的類,加載到內存中了,我再麻煩他幫我干一件事情,把后面的代理類,也加載到內存
?
//參數(shù)二:是一個數(shù)組,在數(shù)組里面寫接口的字節(jié)碼文件對象。
// 如果寫了List,那么表示代理,可以代理List接口里面所有的方法,對這些方法可以增強或者攔截
// 但是,一定要寫ArrayList真實實現(xiàn)的接口
// 假設在第二個參數(shù)中,寫了MyInter接口,那么是錯誤的。
// 因為ArrayList并沒有實現(xiàn)這個接口,那么就無法對這個接口里面的方法,進行增強或攔截
//參數(shù)三:用來創(chuàng)建代理對象的匿名內部類
List proxyList = (List) Proxy.newProxyInstance(
//參數(shù)一:類加載器
MyProxyDemo1.class.getClassLoader(),
//參數(shù)二:是一個數(shù)組,表示代理對象能代理的方法范圍
new Class[]{List.class},
//參數(shù)三:本質就是代理對象
new InvocationHandler() {
@Override
//invoke方法參數(shù)的意義
//參數(shù)一:表示代理對象,一般不用(了解)
//參數(shù)二:就是方法名,我們可以對方法名進行判斷,是增強還是攔截
//參數(shù)三:就是下面第三步調用方法時,傳遞的參數(shù)。
//舉例1:
//list.add("阿瑋好帥");
//此時參數(shù)二就是add這個方法名
//此時參數(shù)三 args[0] 就是 阿瑋好帥
//舉例2:
//list.set(1, "aaa");
//此時參數(shù)二就是set這個方法名
//此時參數(shù)三 args[0] 就是 1 args[1]"aaa"
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//對add方法做一個增強,統(tǒng)計耗時時間
if (method.getName().equals("add")) {
long start = System.currentTimeMillis();
//調用集合的方法,真正的添加數(shù)據(jù)
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);
}
}總結
什么是反射?
- 1.反射就是就是一種通過獲取類的字節(jié)碼文件對象的方式進而獲取類的構造函數(shù),屬性,方法的一種機制
- 2.優(yōu)點就是可以幫助我們簡化一些操作,例如簡化配置文件,缺點就是我們原本設置的私有屬性通過反射也能獲取,不安全
什么是動態(tài)代理?
- 一種無侵入式的給方法增強功能的機制
動態(tài)代理三要素?
- 1.真正干活的對象
- 2.代理對象
- 3.利用代理調用方法
代理對象和真正干活的對象同方法名,使用代理技術后,當調用原方法時不再調用真正干活的對象,而是去調用代理對象,代理對象底層會把真正干活的對象和自己的功能進行拼接,然后再去執(zhí)行。
以上為個人經驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
spring cloud gateway中如何讀取請求參數(shù)
這篇文章主要介紹了spring cloud gateway中如何讀取請求參數(shù)的操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-07-07
fastjson全局日期序列化設置導致JSONField失效問題解決方案
這篇文章主要介紹了fastjson通過代碼指定全局序列化返回時間格式,導致使用JSONField注解標注屬性的特殊日期返回格式失效問題的解決方案2023-01-01

