Java反射和動(dòng)態(tài)代理的使用解讀
1、反射
1.1 反射的概述
是在運(yùn)行狀態(tài)中,不用創(chuàng)建對象就能夠調(diào)用任意一個(gè)類的所有屬性和方法;
1.2 反射作用
反射都是從class字節(jié)碼文件中獲取的內(nèi)容。
- 獲取class字節(jié)碼文件的對象
- 利用反射如何獲取構(gòu)造方法(創(chuàng)建對象)
- 利用反射如何獲取成員變量(賦值,獲取值)
- 利用反射如何獲取成員方法(運(yùn)行)
1.3 獲取字節(jié)碼文件對象的方式
- Class這個(gè)類里面的靜態(tài)方法forName(“全類名”)(最常用)
- 通過class屬性獲取,一般更多的是當(dāng)做參數(shù)進(jìn)行傳遞
- 通過對象獲取字節(jié)碼文件對象,當(dāng)我們已經(jīng)有了這個(gè)類的對象時(shí),才可以使用。
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 字節(jié)碼文件和字節(jié)碼文件對象
- java文件(源代碼階段):就是編寫的java代碼。
- 字節(jié)碼文件(加載階段):就是通過java文件編譯之后的class文件(是在硬盤上真實(shí)存在的,用眼睛能看到的)
- 字節(jié)碼文件對象(運(yùn)行階段):當(dāng)class文件加載到內(nèi)存之后,虛擬機(jī)自動(dòng)創(chuàng)建出來的對象。這個(gè)對象里面至少包含了:構(gòu)造方法,成員變量,成員方法。
反射獲取的是字節(jié)碼文件對象,這個(gè)對象在內(nèi)存中是唯一的。
1.5 獲取構(gòu)造方法
規(guī)則:
- get表示獲取
- Declared表示私有
- 最后的s表示所有,復(fù)數(shù)形式
- 如果當(dāng)前獲取到的是私有的,必須要臨時(shí)修改訪問權(quán)限setAccessible(true),否則無法使用
public class ReflectDemo { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { //1.獲取class字節(jié)碼文件對象 Class clazz = Class.forName("com.ya.reflect.demo02.Student"); //2.獲取構(gòu)造方法 // 2.1 返回所有公共構(gòu)造方法對象的數(shù)組 System.out.println("-----返回所有公共構(gòu)造方法對象的數(shù)組----"); Constructor[] con1 = clazz.getConstructors(); for (Constructor con : con1) { System.out.println(con); } // 2.2 返回所有構(gòu)造方法對象的數(shù)組 System.out.println("-------返回所有構(gòu)造方法對象的數(shù)組------"); Constructor[] con2 = clazz.getDeclaredConstructors(); for (Constructor con : con2) { System.out.println(con); } // 2.3 返回所有構(gòu)造方法對象的數(shù)組 System.out.println("-------返回單個(gè)公共構(gòu)造方法對象------"); Constructor con3 = clazz.getConstructor(String.class); System.out.println(con3); //2.4 獲取指定的空參構(gòu)造 System.out.println("-------獲取指定的空參構(gòu)造------"); Constructor con4 = clazz.getConstructor(); System.out.println(con4); // 2.5 返回所有構(gòu)造方法對象的數(shù)組 System.out.println("-------返回單個(gè)構(gòu)造方法對象------"); Constructor con5 = clazz.getDeclaredConstructor(String.class,int.class); System.out.println(con5); System.out.println("-----獲取構(gòu)造方法的權(quán)限修飾符(返回整數(shù))--------"); // 返回的是整數(shù),public 1,private 2,protected 4,abstract 1024 int modifiers = con5.getModifiers(); System.out.println(modifiers); System.out.println("-----獲取構(gòu)造方法的參數(shù)--------"); Parameter[] parameters = con5.getParameters(); for (Parameter parameter : parameters) { System.out.println(parameter); } System.out.println("--------創(chuàng)建對象-----------"); //暴力反射:表示臨時(shí)取消權(quán)限校驗(yàn) con5.setAccessible(true); // 如果這個(gè)權(quán)限修飾符是私有的,就要取消權(quán)限校驗(yàn) Student stu = (Student) con5.newInstance("張三", 23); System.out.println(stu); } }
1.6 獲取構(gòu)造方法并創(chuàng)建對象
public class ReflectDemo02 { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { //需求1:獲取空參,并創(chuàng)建對象(所創(chuàng)建的對象的屬性是默認(rèn)值) //1.獲取整體的字節(jié)碼文件對象 Class clazz = Class.forName("com.ya.reflect.demo02.Student"); //2.獲取空參的構(gòu)造方法 Constructor con = clazz.getConstructor(); //3.利用空參構(gòu)造方法創(chuàng)建對象 Student stu = (Student) con.newInstance(); System.out.println(stu); System.out.println("----------------------------------"); //需求2:獲取帶參構(gòu)造,并創(chuàng)建對象 //1.獲取整體的字節(jié)碼文件對象 Class clazz2 = Class.forName("com.ya.reflect.demo02.Student"); //2.獲取有參構(gòu)造方法 Constructor con2 = clazz2.getDeclaredConstructor(String.class, int.class); //3.臨時(shí)修改構(gòu)造方法的訪問權(quán)限(暴力反射) con2.setAccessible(true); //4.直接創(chuàng)建對象 Student stu2 = (Student) con2.newInstance("zhangsan", 23); System.out.println(stu2); } }
1.7 獲取成員變量并獲取值和修改值
public class ReflectDemo { public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException { //1.獲取class字節(jié)碼文件的對象 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.獲取單個(gè)的成員變量 System.out.println("-----獲取單個(gè)的成員變量----"); Field fieldName = clazz.getDeclaredField("name"); System.out.println(fieldName); System.out.println("-----獲取成員變量的數(shù)據(jù)類型----"); 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 獲取成員方法
public class ReflectDemo { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException { //1. 獲取class字節(jié)碼文件對象 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.獲取方法的修飾符(返回整數(shù)) System.out.println("-------獲取方法的修飾符(返回整數(shù))-----"); 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 獲取成員方法并運(yùn)行
public class ReflectDemo { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { //1. 獲取class字節(jié)碼文件對象 Class clazz = Class.forName("com.ya.reflect.demo04.Student"); //2. 獲取指定的單一方法 Method eatMethod = clazz.getDeclaredMethod("eat", String.class); // 3. 方法運(yùn)行 System.out.println("-------方法運(yùn)行-----"); Student student = new Student(); eatMethod.setAccessible(true); String result = (String) eatMethod.invoke(student, "漢堡包"); System.out.println(result); } }
2. 動(dòng)態(tài)代理
2.1 動(dòng)態(tài)代理好處
- 無侵入式的給方法增強(qiáng)功能。
- 調(diào)用者(BitStar)–>代理(ProxyUtil)–>對象(Star)
2.2 動(dòng)態(tài)代理三要素
- 接口:代理對象,被代理類和代理類都需要實(shí)現(xiàn)這個(gè)接口(本例是Star)
- 代理類:通過Proxy類動(dòng)態(tài)生成的代理類(本例是ProxyUtil)
- 被代理類:代理類實(shí)例,它會(huì)代替被代理對象處理方法調(diào)用(本例是BigStar)
注:代理可以增強(qiáng)或者攔截的方法都在接口中,接口需要寫在newProxyInstance的第二個(gè)參數(shù)里。
2.3 動(dòng)態(tài)代理簡單實(shí)現(xiàn)
//代理對象 public interface Star { //可以把所有想要被代理的方法定義在接口當(dāng)中 //唱歌 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 + "正在跳舞"); } public String getName() { return name; } public void setName(String name) { this.name = name; } public String toString() { return "BigStar{name = " + name + "}"; } } //代理類 // 創(chuàng)建代理 java.lang.reflect.Proxy類:提供了為對象產(chǎn)生代理對象的方法 public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) 參數(shù)一:用于指定用哪個(gè)類加載器,去加載生成的代理類 參數(shù)二:指定接口,這些接口用于指定生成的代理有哪些方法 參數(shù)三:用來指定生成的代理對象要干什么事情 public class ProxyUtil { /** * 形參:被代理的明星對象 * 返回值:給明星創(chuàng)建的代理 */ public static Star createProxy(BigStar bigStar){ // 創(chuàng)建代理 // 這個(gè)代碼沒有顯示實(shí)現(xiàn)Star接口,但通過 JDK動(dòng)態(tài)代理機(jī)制 隱式地為生成的代理類添加了Star接口的實(shí)現(xiàn),從而可以看作實(shí)現(xiàn)了Star接口 Star star = (Star) Proxy.newProxyInstance( ProxyUtil.class.getClassLoader(),//參數(shù)一:用于指定用哪個(gè)類加載器,去加載生成的代理類 new Class[]{Star.class},//參數(shù)二:指定接口,這些接口用于指定生成的代理有哪些方法 new InvocationHandler() { //參數(shù)三:用來指定生成的代理對象要干什么事情 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { /** * 參數(shù)一:代理的對象,即star變量引用的對象 * 參數(shù)二:要運(yùn)行的方法,sing,dance * 參數(shù)三:調(diào)用sing方法時(shí),傳遞的實(shí)參 */ if("sing".equals(method.getName())){ System.out.println("準(zhǔn)備話筒,收錢"); }else if("dance".equals(method.getName())){ System.out.println("準(zhǔn)備場地,收錢"); } //去找大明星開始唱歌或者跳舞 //代碼的表現(xiàn)形式:調(diào)用大明星里面唱歌或者跳舞的方法 return method.invoke(bigStar,args); } } ); return star; } } //代理測試 public class Test { public static void main(String[] args) { //1. 獲取代理的對象 BigStar bigStar = new BigStar("機(jī)哥"); Star proxy = ProxyUtil.createProxy(bigStar); //2. 調(diào)用唱歌的方法 String result = proxy.sing("只因你太美"); System.out.println(result); } }
2.4 動(dòng)態(tài)代理擴(kuò)展
動(dòng)態(tài)代理,還可以攔截方法。比如:在這個(gè)故事中,經(jīng)紀(jì)人作為代理,如果別人讓邀請大明星去唱歌,打籃球,經(jīng)紀(jì)人就增強(qiáng)功能。但是如果別人讓大明星去掃廁所,經(jīng)紀(jì)人就要攔截,不會(huì)去調(diào)用大明星的方法。
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("攔截,不調(diào)用大明星的方法"); return null; } //如果是其他方法,正常執(zhí)行 return method.invoke(bigStar,args); } } ); return star; } }
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Spring aop+反射實(shí)現(xiàn)電話號(hào)加密
線上項(xiàng)目涉及大量查詢接口中,存在電話號(hào)明文展示不合規(guī)的問題。如果對每個(gè)接口返回結(jié)果中電話號(hào)相關(guān)字段修改相關(guān)代碼邏輯,則工作量較大花費(fèi)時(shí)間多。因此設(shè)計(jì)電話號(hào)加密注解,減少工作量。2021-06-06解決springboot的aop切面不起作用問題(失效的排查)
這篇文章主要介紹了解決springboot的aop切面不起作用問題(失效的排查),具有很好的參考價(jià)值,希望對大家有所幫助。 一起跟隨小編過來看看吧2020-04-04使用@pathvariable與@requestparam碰到的一些問題及解決
這篇文章主要介紹了使用@pathvariable與@requestparam碰到的一些問題及解決,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08打開.properties中文顯示unicode編碼問題以及解決
這篇文章主要介紹了打開.properties中文顯示unicode編碼問題以及解決方案,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01springboot調(diào)用支付寶第三方接口(沙箱環(huán)境)
這篇文章主要介紹了springboot+調(diào)用支付寶第三方接口(沙箱環(huán)境),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-10-10MyBatis Plus構(gòu)建一個(gè)簡單的項(xiàng)目的實(shí)現(xiàn)
這篇文章主要介紹了MyBatis Plus構(gòu)建一個(gè)簡單的項(xiàng)目的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-11-11JAVASE系統(tǒng)實(shí)現(xiàn)抽卡功能
這篇文章主要為大家詳細(xì)介紹了JAVASE系統(tǒng)實(shí)現(xiàn)抽卡功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-11-11Java 是如何利用接口避免函數(shù)回調(diào)的方法
本篇文章主要介紹了Java 是如何利用接口避免函數(shù)回調(diào)的方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-02-02