本來打算寫一篇Mybatis的Mapper代理原始碼簡單閱讀,發現其中有用到動態代理,想著要不先寫一篇動態代理吧,結果發現Jdk的動態代理涉及到反射的知識,所以最后決定寫一篇反射相關的文章,
讀者朋友,在學習技術的時候千萬不要像我寫文章這樣,學一個知識點不要被其他知識點困死,陷入無限回圈中,
對于動態代理和反射大概知道做啥的就能妥妥的看Mybatis的原始碼,
一、對于反射的理解
1. 什么是反射
Java的反射機制是運行狀態中,對于任意一個類,都能夠知道這個類的所有屬性和方法,對于任意一個物件,都能夠呼叫他的任意方法、獲取他的屬性、修改部分類資訊,這種動態獲取資訊以及動態呼叫物件方法的功能成為Java語言的反射機制,
一句話就是:運行時動態呼叫某個類或物件的方法、獲取屬性、修改部分類資訊等,
2. 什么時候用
我理解的第一是獲取某個類的私有屬性或方法,第二是很多框架中通過組態檔加載Java物件的時候需要,
3. 還有什么
不要陷死到理論中,就很不錯了,
二、 反射我來了
小白鼠類:
package test;
/**
* @author 發現更多精彩 關注公眾號:木子的晝夜編程
* 一個生活在互聯網底層,做著增刪改查的碼農,不諳世事的造作
*/
@MyAnotation("測驗注解")
public class Mouse {
public Mouse(){}
private Mouse(String name) {
this.name = name;
}
// 私有變數
private String password = "123456";
@MyFiledAnotation("欄位注解測驗")
public String name = "小小太陽";
//
public void say(String msg){
System.out.println(msg+"叨逼叨叨逼叨...");
}
//
public void run(){
System.out.println("奔跑吧,向著太陽奔跑...");
}
@MyMethodAnotation("方法注解測驗")
private void takeAShower(){
System.out.println("脫光光,洗白白...");
}
@Override
public String toString() {
return "Mouse{" +
"password='" + password + '\'' +
", name='" + name + '\'' +
'}';
}
}
package test;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author 發現更多精彩 關注公眾號:木子的晝夜編程
* 分享一個生活在互聯網底層做著增刪改查的碼農的感悟與學習
* ---》類注解
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnotation {
String value() default "默認值";
}
package test;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author 發現更多精彩 關注公眾號:木子的晝夜編程
* 分享一個生活在互聯網底層做著增刪改查的碼農的感悟與學習
* --》 方法注解
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyMethodAnotation {
String value() default "默認值";
}
package test;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author 發現更多精彩 關注公眾號:木子的晝夜編程
* 分享一個生活在互聯網底層做著增刪改查的碼農的感悟與學習
* --> 欄位注解
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyFiledAnotation {
String value() default "默認值";
}
2.1 獲取Class物件
import test.Mouse;
/**
* @author 發現更多精彩 關注公眾號:木子的晝夜編程
* 一個生活在互聯網底層,做著增刪改查的碼農,不諳世事的造作
*/
public class Test {
public static void main(String[] args) throws ClassNotFoundException {
// 方式1 通過物件獲取
Mouse mouse = new Mouse();
Class<?> c1 = mouse.getClass();
// 方式2 通過forName
Class<?> c2 = Class.forName("test.Mouse");
// 方式3 直接通過類獲取
Class<Mouse> c3 = Mouse.class;
System.out.println("c1 == c2 :" + (c1 == c2));
System.out.println("c2 == c3 :" + (c2 == c3));
// 輸出:
// c1 == c2 :true
// c2 == c3 :true
}
}
2.2 看看Class都有哪些東西
import test.MyFiledAnotation;
import test.MyMethodAnotation;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* @author 發現更多精彩 關注公眾號:木子的晝夜編程
* 一個生活在互聯網底層,做著增刪改查的碼農,不諳世事的造作
*/
public class Test02 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
Class<?> c2 = Class.forName("test.Mouse");
// 1. 類名
System.out.println("包名.類名"+c2.getName());
System.out.println("類名"+c2.getSimpleName());
System.out.println("包名.類名"+c2.getTypeName());
System.out.println("包名.類名"+c2.getCanonicalName());
// 2. 獲取所有屬性 包括private
// c2.getFields() 不能獲取private的屬性
Field[] declaredFields = c2.getDeclaredFields();
for (int i = 0; i < declaredFields.length; i++) {
Field filed = declaredFields[i];
System.out.println("欄位名稱:"+filed.getName());
Annotation[] annotations = filed.getAnnotations();
for (int j = 0; j < annotations.length; j++) {
Annotation annotation = annotations[j];
System.out.println(filed.getName()+"的注解:"+annotation);
if (annotation instanceof MyFiledAnotation) {
MyFiledAnotation my = (MyFiledAnotation) annotation;
System.out.println("可以根據注解獲取自定義相關內容:"+my.value());
}
}
}
//
// 2.2 通過指定欄位名獲取
// 拋例外:java.lang.NoSuchFieldException getField不能獲取private屬性
try {
Field name01 = c2.getField("password");
} catch (Exception e) {
System.out.println("例外:"+e);
}
// 2.3 getDeclaredField可以獲取private的屬性
Field name02 = c2.getDeclaredField("password");
System.out.println("getField可以獲取private屬性:"+name02);
// 3. 獲取所有方法
// getMethods 獲取不到private的方法,getDeclaredMethods可以
Method[] methods = c2.getDeclaredMethods();
for (int i = 0; i < methods.length; i++) {
Method method = methods[i];
String name = method.getName();
// 無關的方法wait equals hashCode toString getClass notify notifyAll 等
if (!"say".equals(name) && !"run".equals(name) && !"takeAShower".equals(name)) {
continue;
}
System.out.println("方法名:"+method.getName());
Annotation[] annotations = method.getAnnotations();
for (int j = 0; j < annotations.length; j++) {
Annotation annotation = annotations[j];
System.out.println(name+"注解:"+annotation);
if(annotation instanceof MyMethodAnotation) {
MyMethodAnotation my = (MyMethodAnotation) annotation;
System.out.println("可以根據注解獲取自定義相關內容:"+my.value());
}
}
}
// 3.2 獲取指定方法 getDeclaredMethod(方法名,可變長度引數串列)
// 與獲取Filed一樣 getMethod(方法名,可變長度引數串列) 也不能獲取
Method say = c2.getDeclaredMethod("say", String.class);
Method run = c2.getDeclaredMethod("run", null);
System.out.println("say:"+say);
System.out.println("run:"+run);
// 4. 特殊的方法 -》 構造方法
// 同理getConstructors不能獲取private的構造方法
Constructor<?>[] constructors = c2.getDeclaredConstructors();
for (int i = 0; i < constructors.length; i++) {
Constructor<?> c = constructors[i];
System.out.println(c);
}
// 4.2 根據建構式引數獲取建構式 一般直接用DeclaredXXX
Constructor<?> constructor = c2.getDeclaredConstructor(String.class);
System.out.println("獲取引數是String的建構式:"+constructor);
}
}
輸出結果:
包名.類名test.Mouse
類名Mouse
包名.類名test.Mouse
包名.類名test.Mouse
欄位名稱:password
欄位名稱:name
name的注解:@test.MyFiledAnotation(value=欄位注解測驗)
可以根據注解獲取自定義相關內容:欄位注解測驗
例外:java.lang.NoSuchFieldException: password
getField可以獲取private屬性:private java.lang.String test.Mouse.password
方法名:run
方法名:say
方法名:takeAShower
takeAShower注解:@test.MyMethodAnotation(value=方法注解測驗)
可以根據注解獲取自定義相關內容:方法注解測驗
say:public void test.Mouse.say(java.lang.String)
run:public void test.Mouse.run()
public test.Mouse()
private test.Mouse(java.lang.String)
獲取引數是String的建構式:private test.Mouse(java.lang.String)
注意:一定要自己親自運行代碼試試 看到的永遠是你覺得會了的,但是不一定是你會了的,
2.3 動態呼叫
import test.Mouse;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* @author 發現更多精彩 關注公眾號:木子的晝夜編程
* 一個生活在互聯網底層,做著增刪改查的碼農,不諳世事的造作
*/
public class Test03 {
public static void main(String[] args) throws Exception {
Class<?> c2 = Class.forName("test.Mouse");
// 1. 動態創建物件
// 1.1 直接呼叫newInstance
Mouse o1 = (Mouse)c2.newInstance();
System.out.println(o1);
// 1.2 通過構造
Mouse o2 = (Mouse)c2.getConstructor().newInstance();
System.out.println(o2);
// can not access a member of class test.Mouse with modifiers "private"
// 應對策略:setAccessible(true)
Constructor<?> dc = c2.getDeclaredConstructor(String.class);
dc.setAccessible(true);
Mouse o3 = (Mouse)dc.newInstance("構造反射創建物件name欄位值");
System.out.println(o3);
// 2. 獲取屬性值/修改屬性值
// 獲取password不能直接獲取
// System.out.println(o3.password);
Field password = c2.getDeclaredField("password");
// 普通屬性不需要設定Accessible
password.setAccessible(true);
System.out.println("反射獲取私有屬性:"+ password.get(o3));
// 修改屬性值
password.setAccessible(true);
password.set(o3, "----------通過反射修改咯--------");
System.out.println("反射修改后的屬性值:"+ password.get(o3));
// 3. 動態呼叫方法(這個非常重要了 動態代理就會用到)
// 3.1 普通方法
Method say = c2.getDeclaredMethod("say", String.class);
say.invoke(o3, "說點什么呢:");
// 3.2 私有方法
Method takeAShower = c2.getDeclaredMethod("takeAShower");
// 解決Class Test03 can not access a member of class test.Mouse with modifiers "private"
takeAShower.setAccessible(true);
takeAShower.invoke(o3);
}
}
2.4 反射獲取注解
其實這個我們已經在上邊寫過了,這里統一寫一下簡單使用,包括欄位、方法、類注解,
import test.Mouse;
import test.MyAnotation;
import test.MyFiledAnotation;
import test.MyMethodAnotation;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* @author 發現更多精彩 關注公眾號:木子的晝夜編程
* 一個生活在互聯網底層,做著增刪改查的碼農,不諳世事的造作
*/
public class Tes04 {
public static void main(String[] args) throws Exception {
Class<?> c2 = Class.forName("test.Mouse");
// 1. 獲取類注解
// 1.1 獲取所有注解
Annotation[] annotations = c2.getAnnotations();
for (int i = 0; i <annotations.length ; i++) {
Annotation annotation = annotations[i];
System.out.println(annotation);
// 通過instanceof 來判斷是否是自己需要的注解
if (annotation instanceof MyAnotation) {
MyAnotation my = (MyAnotation)annotation;
// 獲取自己注解的方法 我之前寫過一個文章 redis鎖防止重復點擊 就用到了自定義注解
System.out.println(my.value());
}
}
// 1.2 獲取指定注解
MyAnotation myannotation = c2.getAnnotation(MyAnotation.class);
System.out.println(myannotation.value());
// 2. 獲取方法注解
// 使用getMethod 會報錯 ---》 Exception in thread "main" java.lang.NoSuchMethodException: test.Mouse.takeAShower()
// 上邊說過了 getMethod不能獲取private方法
Method takeAShower = c2.getDeclaredMethod("takeAShower");
// 2.1 獲取所有注解
Annotation[] MethodAnnotations = takeAShower.getAnnotations();
for (int i = 0; i < MethodAnnotations.length; i++) {
Annotation annotation = MethodAnnotations[i];
System.out.println(annotation);
// 通過instanceof 來判斷是否是自己需要的注解
if (annotation instanceof MyMethodAnotation) {
MyMethodAnotation my = (MyMethodAnotation)annotation;
System.out.println(my.value());
}
}
// 2.2 指定注解獲取
MyMethodAnotation myAnnotation = takeAShower.getAnnotation(MyMethodAnotation.class);
System.out.println(myAnnotation);
// 3. 欄位注解
Field name = c2.getDeclaredField("name");
// 3.1 獲取指定欄位全部注解
Annotation[] FiledAnnotations = name.getAnnotations();
for (int i = 0; i < FiledAnnotations.length; i++) {
Annotation annotation = FiledAnnotations[i];
System.out.println(annotation);
// 通過instanceof 來判斷是否是自己需要的注解
if (annotation instanceof MyFiledAnotation) {
MyFiledAnotation my = (MyFiledAnotation)annotation;
System.out.println(my.value());
}
}
// 3.2 獲取指定型別注解
MyFiledAnotation annotation = name.getAnnotation(MyFiledAnotation.class);
System.out.println("獲取指定注解:"+annotation.value());
}
}
2.5 泛型
看到一篇文章里邊寫了反射操作泛型,我也嘗試簡單寫寫
package test;
import java.beans.IntrospectionException;
import java.util.List;
import java.util.Map;
/**
* @author 發現更多精彩 關注公眾號:木子的晝夜編程
* 一個生活在互聯網底層,做著增刪改查的碼農,不諳世事的造作
*/
public class Person extends CC implements AA {
public Map<String,Mouse> test(List<Short> list, DD<Mouse> dd, Integer i) {
return null;
}
}
interface AA extends BB{
}
interface BB{
}
class CC{
}
package test;
/**
* @author 發現更多精彩 關注公眾號:木子的晝夜編程
* 一個生活在互聯網底層,做著增刪改查的碼農,不諳世事的造作
*/
public class DD<T> {
}
import test.DD;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
/**
* @author 發現更多精彩 關注公眾號:木子的晝夜編程
* 一個生活在互聯網底層,做著增刪改查的碼農,不諳世事的造作
*/
public class Test05 {
public static void main(String[] args) throws Exception {
Class<?> c2 = Class.forName("test.Person");
// 1. 獲取類實作的介面
Type[] interfaces = c2.getGenericInterfaces();
for (int i = 0; i < interfaces.length; i++) {
System.out.println(interfaces[i]);
}
// 2. 獲取類繼承的父類 如果沒有extends CC 這里會獲取到java.lang.Object
// 也就是說他至少會輸出一個類 java.lang.Object兜底
Type t = c2.getGenericSuperclass();
System.out.println(t);
// 3.1 某個方法的引數泛型
Method test = c2.getMethod("test", List.class, DD.class, Integer.class);
Type[] genericParameterTypes = test.getGenericParameterTypes();
for (int i = 0; i < genericParameterTypes.length; i++) {
Type gt = genericParameterTypes[i];
System.out.println("沒過濾:"+gt);
if (gt instanceof ParameterizedType) {
System.out.println("過濾:"+gt);
ParameterizedType pgt = (ParameterizedType) gt;
Type[] arg = pgt.getActualTypeArguments();
for (int j = 0; j < arg.length; j++) {
System.out.println("過濾后獲取的真實泛型型別:"+arg[j]);
}
}
}
// 3.2 某個方法的回傳值泛型
Type grt = test.getGenericReturnType();
System.out.println("grt:"+grt);
if (grt instanceof ParameterizedType) {
ParameterizedType pgt = (ParameterizedType) grt;
Type[] arg = pgt.getActualTypeArguments();
for (int j = 0; j < arg.length; j++) {
System.out.println("回傳真實泛型型別:"+j+" "+arg[j]);
}
}
}
}
三、擴展
3.1 org.reflections.reflections
reflections可以掃描出指定包下的指定類
示例:
pom.xml引入
<dependencies>
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>0.9.11</version>
</dependency>
</dependencies>
package com.freeedu.test;
/**
* @author 發現更多精彩 關注公眾號:木子的晝夜編程
* 一個生活在互聯網底層,做著增刪改查的碼農,不諳世事的造作
*/
public class Father {
}
package com.freeedu.test;
import com.sun.istack.internal.NotNull;
/**
* @author 發現更多精彩 關注公眾號:木子的晝夜編程
* 一個生活在互聯網底層,做著增刪改查的碼農,不諳世事的造作
* @create 2021-10-10 15:07
*/
@MyClassAnnotation
public class Person extends Father{
@MyMethodAnotation
public void test01(String str, Integer integer) {
}
@MyMethodAnotation
public String test02(@MyParamterAnotation String str, Integer integer) {
return null;
}
}
package com.freeedu.test;
/**
* @author 發現更多精彩 關注公眾號:木子的晝夜編程 分享一個生活在互聯網底層做著增刪改查的碼農的感悟與學習
*/
public @interface MyClassAnnotation {
}
package com.freeedu.test;
import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
/**
* @author 發現更多精彩 關注公眾號:木子的晝夜編程 分享一個生活在互聯網底層做著增刪改查的碼農的感悟與學習
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={METHOD})
public @interface MyMethodAnotation {
}
package com.freeedu.test;
import java.lang.annotation.*;
import static java.lang.annotation.ElementType.METHOD;
/**
* @author 發現更多精彩 關注公眾號:木子的晝夜編程 分享一個生活在互聯網底層做著增刪改查的碼農的感悟與學習
* @create 2021-10-10 15:39
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={ElementType.PARAMETER})
public @interface MyParamterAnotation {
}
下邊我簡單寫集中我自己玩兒的方法,如果有其他需要直接.提示猜測有沒有對應方法
或者點進原始碼看看,你需要的大概率這里都能提供
import com.freeedu.test.*;
import com.sun.istack.internal.NotNull;
import org.reflections.ReflectionUtils;
import org.reflections.Reflections;
import org.reflections.scanners.MethodParameterScanner;
import org.reflections.scanners.SubTypesScanner;
import org.reflections.scanners.TypeAnnotationsScanner;
import org.reflections.util.ConfigurationBuilder;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Set;
/**
* @author 發現更多精彩 關注公眾號:木子的晝夜編程
* 一個生活在互聯網底層,做著增刪改查的碼農,不諳世事的造作
*/
public class Test {
public static void main(String[] args) {
// 初始化 默認掃描com.freeedu.test包
Reflections reflections = new Reflections("com.freeedu.test");
// 1. 掃描某些類的子類 Father的子類
Set<Class<? extends Father>> subTypesOf = reflections.getSubTypesOf(Father.class);
subTypesOf.stream().forEach(System.out::println);
// 2. 根據方法引數掃描符合引數的方法
//掃描不同的型別 需要不同的掃描工具
//需要指定 setScanners(new MethodParamterScanner()) 否則報錯:Scanner MethodParameterScanner was not configured
reflections = new Reflections(
new ConfigurationBuilder()
.forPackages("com.freeedu.test") // 指定掃描包
// 指定多中掃描工具
.setScanners(new MethodParameterScanner(),
new TypeAnnotationsScanner(),
new SubTypesScanner())
);
Set<Method> methodsMatchParams = reflections.getMethodsMatchParams(String.class, Integer.class);
methodsMatchParams.stream().forEach(System.out::println);
// 3. 獲取類上有指定注解的類 class com.freeedu.test.Person
// 同理可以獲取方法、屬性上都指定注解的方法和屬性
Set<Class<?>> typesAnnotatedWith = reflections.getTypesAnnotatedWith(MyClassAnnotation.class);
typesAnnotatedWith.forEach(System.out::println);
// 4. 獲取指定回傳值的方法
Set<Method> methodsReturn = reflections.getMethodsReturn(String.class);
methodsReturn.forEach(System.out::println);
// 其實官方已經給了我們一個很好用的Utils ---> ReflectionUtils
// 獲取某個類的方法 指定可見性+入參個數+前綴
Set<Method> test = ReflectionUtils.getAllMethods(Person.class,
ReflectionUtils.withModifier(Modifier.PUBLIC), // 修飾符
ReflectionUtils.withPrefix("test"), // 方法前綴
ReflectionUtils.withParametersCount(2),// 引數總數
ReflectionUtils.withReturnType(String.class),// 回傳值型別
ReflectionUtils.withParameters(String.class, Integer.class),// 方法引數型別
ReflectionUtils.withAnnotation(MyMethodAnotation.class),// 方法注解 為什么不識別 我加上了呀~~!
ReflectionUtils.withAnyParameterAnnotation(MyParamterAnotation.class)// 方法引數注解
// 還有各式各樣的過濾 有興趣或有需要的朋友可以自己找找自己感興趣的
);
System.out.println("符合條件的方法:");
test.forEach(System.out::println);
//
}
}
3.2 org.javassist.javassist
javassist是一個很牛X的東西,
先搞一個demo大家瞅瞅,傳入一個Map key值對應物體類TestPO欄位名稱 Value值對應物體類TestPO欄位值
我們怎么把資料設定到物體類中呢
/**
* @author 發現更多精彩 關注公眾號:木子的晝夜編程
* 一個生活在互聯網底層,做著增刪改查的碼農,不諳世事的造作
*/
public class TestPO {
public Integer id;
public String name;
public Integer age;
@Override
public String toString() {
return "TestPO{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
手動編碼(硬)+反射編碼(軟)+高級反射編碼(軟變硬)

package test;
import java.util.Map;
/**
* @author 發現更多精彩 關注公眾號:木子的晝夜編程
* 一個生活在互聯網底層,做著增刪改查的碼農,不諳世事的造作
* @create 2021-10-10 19:41
*/
public abstract class AbstractTransferHelper {
public abstract Object transfer(Map map) throws Exception;
}
package test;
import javassist.*;
import java.io.IOException;
import java.lang.reflect.Field;
/**
* @author 發現更多精彩 關注公眾號:木子的晝夜編程
* 一個生活在互聯網底層,做著增刪改查的碼農,不諳世事的造作
* @create 2021-10-10 20:10
*/
public class TransferUtil {
// 這里用到了javaassist
// 這個就是有近似于寫死代碼的性能 有近似于反射的適配性 如果再加欄位 這里是不用修改的
// 判空什么的就先不做了 主要講使用方式
public static AbstractTransferHelper getTransferHelper(Class clazz) throws NotFoundException, CannotCompileException, IllegalAccessException, InstantiationException, IOException {
ClassPool pool = ClassPool.getDefault();
pool.appendSystemPath();
// 導包
//import java.util.HashMap;
//import java.util.Map;
pool.importPackage("java.util.Map");
pool.importPackage("java.util.HashMap");
//import test.AbstractTransferHelper
pool.importPackage("test.AbstractTransferHelper");
//import test.TestPO;
pool.importPackage(clazz.getName());
pool.importPackage(AbstractTransferHelper.class.getName());
// 父類
CtClass superClass = pool.getCtClass(AbstractTransferHelper.class.getName());
// 自定義動態創建的類名
String className = clazz.getName()+"TransferHelper";
// 創建類 指定父類superClass
// Class XXXTransferHelper extends AbstractTransferHelper
CtClass myclass = pool.makeClass(className, superClass);
// 建構式 public XXXTransferHelper(){}
CtConstructor ctConstructor = new CtConstructor(new CtClass[0], myclass);
ctConstructor.setBody("{}");
myclass.addConstructor(ctConstructor);
// 方法---
StringBuilder sb = new StringBuilder();
sb.append("public Object transfer(Map map) throws Exception {\n");
// 類似:TestPO obj = new TestPO();
sb.append(clazz.getName() +" obj = new "+clazz.getName()+"();\n");
// 設定屬性值
Field[] fields = clazz.getFields();
String strF = "obj.%s = map.get(\"%s\") == null ? null : String.valueOf(map.get(\"%s\"));\n";
String strI = "obj.%s = map.get(\"%s\") == null ? null : Integer.valueOf(map.get(\"%s\").toString());\n";
for (int i = 0; i < fields.length; i++) {
Field field = fields[i];
String name = field.getName();
Class<?> type = field.getType();
// 這里只寫String Integer 型別 其他我就不寫了
if (type == String.class) {
// 類似obj.name = map.get("name") == null ? null : String.valueOf(map.get("name"));
String format = String.format(strF, field.getName(), field.getName(), field.getName());
sb.append(format);
} else if (type == Integer.class) {
// 類似obj.name = map.get("name") == null ? null : Integer.valueOf(map.get("name").toString());
String format = String.format(strI, field.getName(), field.getName(), field.getName());
sb.append(format);
}
}
sb.append("return obj;\n");
sb.append("}");
// 創建方法
CtMethod method = CtMethod.make(sb.toString(), myclass);
myclass.addMethod(method);
// 創建物體
Class aClass = myclass.toClass();
// myclass.writeFile("E:\\MyNote\\test");
System.out.println(aClass);
return (AbstractTransferHelper)aClass.newInstance();
}
}
import test.AbstractTransferHelper;
import test.TestPO;
import test.TransferUtil;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
/**
* @author 發現更多精彩 關注公眾號:木子的晝夜編程
* 一個生活在互聯網底層,做著增刪改查的碼農,不諳世事的造作
* @create 2021-10-10 16:24
*/
public class Test {
public static void main(String[] args) throws Exception {
// 引數
/*Map<String,Object> map = new HashMap<>();
long start = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
TestPO res = Method02(map, TestPO.class);
}
long end = System.currentTimeMillis();
System.out.println(end-start);*/
Map<String,Object> map = new HashMap<>();
AbstractTransferHelper helper = TransferUtil.getTransferHelper(TestPO.class);
long start = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
TestPO res = Method03(map, helper);
}
long end = System.currentTimeMillis();
System.out.println(end-start);
}
// 手動編碼 一百萬次30毫秒左右
private static TestPO Method01(Map<String, Object> map, Class<TestPO> testPOClass) {
TestPO res = new TestPO();
res.id = map.get("id") == null ? null : Integer.valueOf(map.get("id").toString()) ;
res.name = map.get("name") == null ? null : String.valueOf(map.get("name").toString()) ;
res.age = map.get("age") == null ? null : Integer.valueOf(map.get("age").toString()) ;
return res;
}
// 反射 一百萬次200~300毫秒
// 這個有什么好處呢 如果添加欄位 這個方法是不需要修改的 而Method01的硬編碼是需要修改的
private static <PO> PO Method02(Map<String, Object> map, Class<PO> clazz) throws Exception {
Object res = clazz.newInstance();
Field[] fields = clazz.getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
Field field = fields[i];
field.setAccessible(true);
// 獲取欄位名稱
String name = field.getName();
// 獲取欄位型別
Class<?> type = field.getType();
// 從Map中獲取值
if (type == Integer.class) {
field.set(res, map.get(name) == null ? null : Integer.valueOf(Integer.valueOf(map.get(name).toString())));
} else if(type == String.class){
field.set(res, map.get(name) == null ? null : String.valueOf(String.valueOf(map.get(name))));
}
}
return (PO) res;
}
// 反射 高級版 --> 軟變硬 一百萬次40毫秒左右
private static <PO> PO Method03(Map<String, Object> map, AbstractTransferHelper helper) throws Exception {
return (PO) helper.transfer(map);
}
}
javaassist的功能遠遠大于我寫的這個demo 有興趣的讀者自行研究~~
四、嘮嘮
我看過一些原始碼,其實一般都只會用到
反射可能會帶來一些安全問題,我們一般在重構專案或者是處理一個很復雜的業務的時候才會使用,一般情況我們寫業務代碼用不到反射,
源代碼地址:
https://github.com/githubforliming/ReflectionTest.git
https://github.com/githubforliming/JavassistTest.git
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/311720.html
標籤:java
