Java反射详解,学以致用,实战案例(AOP修改参数、Mybatis拦截器实现自动填充)2

简介: Java反射详解,学以致用,实战案例(AOP修改参数、Mybatis拦截器实现自动填充)

Java反射详解,学以致用,实战案例(AOP修改参数、Mybatis拦截器实现自动填充)1:https://developer.aliyun.com/article/1394599

3.1、反射获取运行时类构造方法并使用

class获取构造方法的相关API

 // 获取所有的构造函数
 Constructor<?>[] getConstructors()
 ​
 // 获取 public或 private 修饰的狗赞函数,只要参数匹配即可
 Constructor<?>[] getDeclaredConstructors()
 ​
 // 获取所有 public 修饰的 构造函数
 Constructor<T> getConstructor(Class<?>... parameterTypes)
     
 //调用此方法,创建对应的运行时类的对象。
 public T newInstance(Object ... initargs)

newInstance():调用此方法,创建对应的运行时类的对象。内部调用了运行时类的空参的构造器。

要想此方法正常的创建运行时类的对象,要求:

  • 运行时类必须提供空参的构造器;
  • 空参的构造器的访问权限得够。通常,设置为 public。

为什么要 javabean 中要求提供一个 public 的空参构造器?

原因: 1、便于通过反射,创建运行时类的对象;2、便于子类继承此运行时类时,默认调用 super() 时,保证父类有此构 造器。

想要更详细的了解,建议去看生成的字节码文件,在那里能够给出你答案。

测试

 /**
  * @description:
  * @author: Ning Zaichun
  * @date: 2022年09月17日 1:17
  */
 public class ConstructorTest {
 ​
     /**
      * 获取公有、私有的构造方法 并调用创建对象
      */
     @Test
     public void test1() throws Exception {
         Class<?> aClass = Class.forName("com.nzc.Student");
 ​
         System.out.println("======获取全部public 修饰的构造方法=========");
         Constructor<?>[] constructors = aClass.getConstructors();
         for (Constructor<?> constructor : constructors) {
             System.out.println(constructor);
         }
         System.out.println("======获取 public、private 修饰的构造方法,只要参数匹配即可=========");
         /**
          * 这里的参数 Class<?>... parameterTypes 填写的是参数的类型,而并非某个准确的值信息
          */
         Constructor<?> constructor = aClass.getDeclaredConstructor(String.class);
         System.out.println(constructor);
         // 因为此构造函数是 private 修饰,如果不设置暴力反射,则没有权限访问。
         // 这里setAccessible(true) 是设置暴力反射,如果不设置,则会报错,
         constructor.setAccessible(true);
         // 这里调用的有参构造,所以在调用 newInstance 方法时,也需要填写参数
         Object o1 = constructor.newInstance("宁在春");
         constructor.setAccessible(false);
         System.out.println("o1===>"+o1);
 ​
         /**
          * 如果需要获取有参构造,只需要填写对应的参数类型即可,
          * 获取无参构造,填null或不填都可。
          */
         Constructor<?> constructor1 = aClass.getConstructor();
         Constructor<?> constructor2 = aClass.getConstructor(String.class,String.class);
         System.out.println("无参构造==>"+constructor1);
         System.out.println("有参构造==>"+constructor2);
         Object o2 = constructor1.newInstance();
         Object o3 = constructor2.newInstance("宁在春2","xxxx社会");
         System.out.println("o2===>"+o2);
         System.out.println("o3===>"+o3);
     }
 }

既然能够获取到构造方法,那么也就是可以使用的,用Constructor.newInstance()方法来调用构造方法即可,在下列的打印信息中,也可以看出来确实如此,如果明确要获取为Student对象的话,进行强转即可。

 ======获取全部public 修饰的构造方法=========
 public com.nzc.Student(java.lang.String,java.lang.String)
 public com.nzc.Student()
 ======获取 public、private 修饰的构造方法,只要参数匹配即可=========
 private com.nzc.Student(java.lang.String)
 o1===>Student(username=宁在春, school=null, age=null)
 无参构造==>public com.nzc.Student()
 有参构造==>public com.nzc.Student(java.lang.String,java.lang.String)
 o2===>Student(username=null, school=null, age=null)
 o3===>Student(username=宁在春2, school=xxxx社会, age=null)

3.2、反射获取运行时类成员变量信息

class对象中获取类成员信息使用到的API

scss

 Field[] getFields();
  
 Field[] getDeclaredFields();
 ​
 Field getDeclaredField(String name);
 ​
 public native Class<? super T> getSuperclass();

这里的 Field 类对象,其实就是表示类对象中的成员属性,不过还有多了很多其他在反射时需要用到的属性和API罢了~

3.2.1、获取私有公有类成员信息

为了有更好的对比,我先编写了一段不使用反射时的正常操作。

 /**
      * 不使用反射的进行操作
      */
 @Test
 public void test1() {
     Student student = new Student();
     student.setUsername("username");
     student.setSchool("xxxx社会");
     student.setScore(100.0);
     // 永远三岁的小伙子 哈哈 🤡
     student.setAge(3);
     student.setSex("男");
 ​
     System.out.println("student信息===>" + student);
     // out:student信息===>Student(super=Person(sex=男, age=3), username=username, school=xxxx社会, score=100.0)
 }

现在我再使用反射来实现上述操作~

在实现之前,还是先来看看如何获取公有、私有的成员变量吧

 @Test
 public void test2() throws Exception {
     Class<?> stuClass = Class.forName("com.nzc.Student");
 ​
     System.out.println("========获取所有 public 修饰的成员属性(包括父类属性)=====");
     Field[] fields = stuClass.getFields();
     for (Field field : fields) {
         System.out.println(field);
     }
     //public java.lang.String com.nzc.Student.school
     //public java.lang.Integer com.nzc.Person.age
 ​
     System.out.println("========获取所有(public、private、protected等修饰的)属性成员(不包括父类成员属性)=====");
     Field[] fields1 = stuClass.getDeclaredFields();
     for (Field field : fields1) {
         System.out.println(field);
     }
     //private java.lang.String com.nzc.Student.username
     //public java.lang.String com.nzc.Student.school
     //private java.lang.Double com.nzc.Student.score
 ​
     System.out.println("========通过反射,获取对象指定的属性=====");
     Field username = stuClass.getDeclaredField("username");
     System.out.println("username===>" + username);
 }

但是你发现没有,这无法获取到父类的成员变量信息,父类的信息,我们该如何获取呢?

3.2.2、获取父类成员属性信息

其实一样的,我们也是要获取到父类的Class对象,在Class API中有一个getSuperClass()方法可以获取到父类的class对象,其他的操作都是一样的~

 @Test
 public void test5() throws ClassNotFoundException {
     Class<?> stuClass = Class.forName("com.nzc.Student");
     System.out.println("========获取所有属性成员(包括父类成员属性)=====");
     Class clazz = stuClass;
     List<Field> fieldList = new ArrayList<>();
     while (clazz != null) {
         fieldList.addAll(new ArrayList<>(Arrays.asList(clazz.getDeclaredFields())));
         clazz = clazz.getSuperclass();
     }
     Field[] fields2 = new Field[fieldList.size()];
     fieldList.toArray(fields2);
     for (Field field : fields2) {
         System.out.println(field);
     }
     //private java.lang.String com.nzc.Student.username
     //public java.lang.String com.nzc.Student.school
     //private java.lang.Double com.nzc.Student.score
     //private java.lang.String com.nzc.Person.sex
     //public java.lang.Integer com.nzc.Person.age
 }

到这里我们已经知道如何获取到类的成员变量信息了,就可以来看看如何给它们设置值,或者通过反射的方式来获取到值了。

3.2.3、设置或修改类成员变量值

 @Test
 public void test3() throws Exception {
     Class<?> stuClass = Class.forName("com.nzc.Student");
     System.out.println("获取到 filed 后,设置值或修改值");
     Constructor<?> constructor = stuClass.getConstructor();
     Object o = constructor.newInstance();
 ​
     //操作pubilc 属性
     Field school = stuClass.getDeclaredField("school");
     school.set(o,"xxxx社会");
     System.out.println(o);
     //Student(super=Person(sex=null, age=null), username=null, school=xxxx社会, score=null)
     // 另外既然可以设置,那么自然也是可以获取的
     System.out.println(school.get(o));
     //xxxx社会
 ​
     // 操作 private 修饰的成员属性
     Field name = stuClass.getDeclaredField("username");
     // setAccessible(true) 因为是获取private 修饰的成员变量
     // 不设置暴力反射 是无法获取到的,会直接报 IllegalAccessException 异常
     name.setAccessible(true);
     name.set(o,"Ningzaichun");
     name.setAccessible(false);
     System.out.println(o);
     //Student(super=Person(sex=null, age=null), username=Ningzaichun, school=xxxx社会, score=null)
 }

其实看到这里对于反射已经有个大概的印象了,并且这都是比较平常且实用的一些方法。

3.2.4、获取成员变量注解信息

 @Test
 public void test3() throws Exception {
     Class<?> stuClass = Class.forName("com.nzc.Student");
     System.out.println("获取到 filed 后,设置值或修改值");
     Constructor<?> constructor = stuClass.getConstructor();
     Object o = constructor.newInstance();
 ​
     //操作pubilc 属性
     Field school = stuClass.getDeclaredField("school");
     school.set(o,"xxxx社会");
     System.out.println(o);
     //Student(super=Person(sex=null, age=null), username=null, school=xxxx社会, score=null)
     // 另外既然可以设置,那么自然也是可以获取的
     System.out.println(school.get(o));
     //xxxx社会
 ​
     // 操作 private 修饰的成员属性
     Field name = stuClass.getDeclaredField("username");
     // setAccessible(true) 因为是获取private 修饰的成员变量
     // 不设置暴力反射 是无法获取到的,会直接报 IllegalAccessException 异常
     name.setAccessible(true);
     name.set(o,"Ningzaichun");
     name.setAccessible(false);
     System.out.println(o);
     //Student(super=Person(sex=null, age=null), username=Ningzaichun, school=xxxx社会, score=null)
 }

关于getAnnotation、getDeclaredAnnotation 的分析大家可以看看下面这篇文章@Repeatable详解-getAnnotation、getDeclaredAnnotation获取不到对象

对于想要更详细的了解Java注解的朋友,大家可以继续找找相关资料。

3.3、反射获取运行时类对对象方法信息并调用

Class对象中使用的相关API

 Method[] getMethods();
 ​
 Method getMethod(String name, Class<?>... parameterTypes);
 ​
 Method[] getDeclaredMethods();
 ​
 Method getDeclaredMethod(String name, Class<?>... parameterTypes);
 ​
 // 获取方法返回值类型
 Class<?> getReturnType();
 ​
 // obj – 调用底层方法的对象 
 // args – 用于方法调用的参数 
 // return 使用参数args在obj上调度此对象表示的方法的结果
 Object invoke(Object obj, Object... args)

3.3.1、获取对象方法

获取公有、私有方法

 @Test
 public void test1() throws Exception {
     Class<?> stuClass = Class.forName("com.nzc.Student");
 ​
     System.out.println("========获取所有public修饰的方法======");
     Method[] methods = stuClass.getMethods();
     for (Method method : methods) {
         System.out.println(method);
     }
     //public boolean com.nzc.Student.equals(java.lang.Object)
     //public java.lang.String com.nzc.Student.toString()
     //public int com.nzc.Student.hashCode()
     //public void com.nzc.Student.hello()
     //public void com.nzc.Student.say(java.lang.String)
     //public java.lang.Double com.nzc.Student.getScore()
     //public void com.nzc.Student.setScore(java.lang.Double)
     //public void com.nzc.Student.setSchool(java.lang.String) .... 还有一些没写出来了
 ​
     System.out.println("========获取对象【指定的public修饰的方法】======");
     // 第一个参数为方法名,此处是获取public修饰的无参方法
     Method hello = stuClass.getMethod("hello");
     System.out.println("hello===>"+hello);
     // 带参方法,第二个参数填写【参数类型】
     Method say = stuClass.getMethod("say", String.class);
     System.out.println("say===>"+say);
     //hello===>public void com.nzc.Student.hello()
     //say===>public void com.nzc.Student.say(java.lang.String)
 ​
 ​
     // 参数为 方法名,此处是获取 private 修饰的无参方法
     // stuClass.getDeclaredMethod("");
     Method myScore = stuClass.getDeclaredMethod("myScore", Double.class);
     System.out.println("myScore==>"+myScore);
     //myScore==>private void com.nzc.Student.myScore(java.lang.Double)
 ​
     System.out.println("=======获取所有的方法=======");
     Method[] declaredMethods = stuClass.getDeclaredMethods();
     for (Method declaredMethod : declaredMethods) {
         System.out.println(declaredMethod);
     }
     //public void com.nzc.Student.say(java.lang.String)
     //private void com.nzc.Student.myScore(java.lang.Double)
     //public java.lang.Double com.nzc.Student.getScore()
     //public void com.nzc.Student.setScore(java.lang.Double)
     //protected boolean com.nzc.Student.canEqual(java.lang.Object)
 }

既然能获取到,那么也是能够调用的啦~,

3.3.2、调用对象方法

还记得经常能看到的一个invoke()方法吗,这里就是调用method.invoke()方法来实现方法的调用。

 @Test
 public void test2() throws Exception {
     Class<?> stuClass = Class.forName("com.nzc.Student");
     Constructor<?> constructor = stuClass.getConstructor();
     Object o = constructor.newInstance();
 ​
     Method myScore = stuClass.getDeclaredMethod("myScore", Double.class);
     myScore.setAccessible(true);
     // 调用方法
     myScore.invoke(o, 99.0);
     // 相当于 Student student= new Student();
     // student.myScore(99.0);
     myScore.setAccessible(false);
 }

之前阐述了 Method它本身就记录了方法的一切信息,我们实现调用也就是第一步罢了,说它可以获取到当时定义方法的一切都不为过,接下来一步一步来说吧。

Java反射详解,学以致用,实战案例(AOP修改参数、Mybatis拦截器实现自动填充)3:https://developer.aliyun.com/article/1394602


目录
相关文章
|
1月前
|
Java 数据库连接 Maven
mybatis使用一:springboot整合mybatis、mybatis generator,使用逆向工程生成java代码。
这篇文章介绍了如何在Spring Boot项目中整合MyBatis和MyBatis Generator,使用逆向工程来自动生成Java代码,包括实体类、Mapper文件和Example文件,以提高开发效率。
116 2
mybatis使用一:springboot整合mybatis、mybatis generator,使用逆向工程生成java代码。
|
28天前
|
搜索推荐 Java 数据库连接
Java|在 IDEA 里自动生成 MyBatis 模板代码
基于 MyBatis 开发的项目,新增数据库表以后,总是需要编写对应的 Entity、Mapper 和 Service 等等 Class 的代码,这些都是重复的工作,我们可以想一些办法来自动生成这些代码。
30 6
|
28天前
|
存储 Java
[Java]反射
本文详细介绍了Java反射机制的基本概念、使用方法及其注意事项。首先解释了反射的定义和类加载过程,接着通过具体示例展示了如何使用反射获取和操作类的构造方法、方法和变量。文章还讨论了反射在类加载、内部类、父类成员访问等方面的特殊行为,并提供了通过反射跳过泛型检查的示例。最后,简要介绍了字面量和符号引用的概念。全文旨在帮助读者深入理解反射机制及其应用场景。
20 0
[Java]反射
|
2月前
|
安全 Java 索引
Java——反射&枚举
本文介绍了Java反射机制及其应用,包括获取Class对象、构造方法、成员变量和成员方法。反射允许在运行时动态操作类和对象,例如创建对象、调用方法和访问字段。文章详细解释了不同方法的使用方式及其注意事项,并展示了如何通过反射获取类的各种信息。此外,还介绍了枚举类型的特点和使用方法,包括枚举的构造方法及其在反射中的特殊处理。
65 9
Java——反射&枚举
|
1月前
|
安全 Java 测试技术
🌟Java零基础-反射:从入门到精通
【10月更文挑战第4天】本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
26 2
|
2月前
|
缓存 前端开发 Java
【Java面试题汇总】Spring,SpringBoot,SpringMVC,Mybatis,JavaWeb篇(2023版)
Soring Boot的起步依赖、启动流程、自动装配、常用的注解、Spring MVC的执行流程、对MVC的理解、RestFull风格、为什么service层要写接口、MyBatis的缓存机制、$和#有什么区别、resultType和resultMap区别、cookie和session的区别是什么?session的工作原理
【Java面试题汇总】Spring,SpringBoot,SpringMVC,Mybatis,JavaWeb篇(2023版)
|
2月前
|
安全 Java API
【Java面试题汇总】Java基础篇——String+集合+泛型+IO+异常+反射(2023版)
String常量池、String、StringBuffer、Stringbuilder有什么区别、List与Set的区别、ArrayList和LinkedList的区别、HashMap底层原理、ConcurrentHashMap、HashMap和Hashtable的区别、泛型擦除、ABA问题、IO多路复用、BIO、NIO、O、异常处理机制、反射
【Java面试题汇总】Java基础篇——String+集合+泛型+IO+异常+反射(2023版)
|
1月前
|
IDE Java 编译器
java的反射与注解
java的反射与注解
16 0
|
2月前
|
Java 程序员 编译器
Java的反射技术reflect
Java的反射技术允许程序在运行时动态加载和操作类,基于字节码文件构建中间语言代码,进而生成机器码在JVM上执行,实现了“一次编译,到处运行”。此技术虽需更多运行时间,但广泛应用于Spring框架的持续集成、动态配置及三大特性(IOC、DI、AOP)中,支持企业级应用的迭代升级和灵活配置管理,适用于集群部署与数据同步场景。
|
2月前
|
Java 数据库连接 数据格式
【Java笔记+踩坑】Spring基础2——IOC,DI注解开发、整合Mybatis,Junit
IOC/DI配置管理DruidDataSource和properties、核心容器的创建、获取bean的方式、spring注解开发、注解开发管理第三方bean、Spring整合Mybatis和Junit
【Java笔记+踩坑】Spring基础2——IOC,DI注解开发、整合Mybatis,Junit
下一篇
无影云桌面