• 注册
  • 程序人生 程序人生 关注:12 内容:2414

    面试官问你反射,你能回答多少

  • 查看作者
  • 打赏作者
  • 当前位置: 职业司 > 职业宝典 > 程序人生 > 正文
    • 程序人生
    • 本来打算写一篇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 有兴趣的读者自行研究~~

      四、唠唠

      我看过一些源码,其实一般都只会用到1. 动态创建实例 2. 动态调用方法

      反射可能会带来一些安全问题,我们一般在重构项目或者是处理一个很复杂的业务的时候才会使用,一般情况我们写业务代码用不到反射。

      如果面试的时候问到了面试,你最好能提前准备一个工作中用反射解决项目问题的例子。

      还能跟面试官谈到设计模式(动态代理),如果你都懂得话,可以跟面试官谈Java动态代理与CGLIB动态代理。

      还可以结合你看过的某些框架源码中哪儿用到了反射,这样会让面试官觉得你是真的用过学过,而不是背过。

      源代码地址:

      链接

      链接

      请登录之后再进行评论

      登录

      手机阅读天地(APP)

      • 微信公众号
      • 微信小程序
      • 安卓APP
      手机浏览,惊喜多多
      匿名树洞,说我想说!
      问答悬赏,VIP可见!
      密码可见,回复可见!
      即时聊天、群聊互动!
      宠物孵化,赠送礼物!
      动态像框,专属头衔!
      挑战/抽奖,金币送不停!
      赶紧体会下,不会让你失望!
    • 实时动态
    • 签到
    • 做任务
    • 发表内容
    • 偏好设置
    • 到底部
    • 帖子间隔 侧栏位置:
    • 还没有账号?点这里立即注册