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


目录
相关文章
|
Java Linux 定位技术
Minecraft配置文件参数说明(JAVA服务器篇)
Minecraft JAVA版服务器启动后会生成server.properties配置文件,位于minecraft_server/根目录下。该文件包含多项关键设置,如游戏模式(gamemode)、最大玩家数(max-players)、难度(difficulty)等。此文档详细说明了各配置项的功能与默认值,帮助用户高效管理服务器环境。
4132 62
java中一个接口A,以及一个实现它的类B,一个A类型的引用对象作为一个方法的参数,这个参数的类型可以是B的类型吗?
本文探讨了面向对象编程中接口与实现类的关系,以及里氏替换原则(LSP)的应用。通过示例代码展示了如何利用多态性将实现类的对象传递给接口类型的参数,满足LSP的要求。LSP确保子类能无缝替换父类或接口,不改变程序行为。接口定义了行为规范,实现类遵循此规范,从而保证了多态性和代码的可维护性。总结来说,接口与实现类的关系天然符合LSP,体现了多态性的核心思想。
753 0
|
Java 数据库连接 Maven
mybatis使用一:springboot整合mybatis、mybatis generator,使用逆向工程生成java代码。
这篇文章介绍了如何在Spring Boot项目中整合MyBatis和MyBatis Generator,使用逆向工程来自动生成Java代码,包括实体类、Mapper文件和Example文件,以提高开发效率。
825 2
mybatis使用一:springboot整合mybatis、mybatis generator,使用逆向工程生成java代码。
|
Java
实现java执行kettle并传参数
实现java执行kettle并传参数
433 1
|
监控 Java 数据管理
java会话跟踪和拦截器过滤器
本文介绍了Web开发中的会话跟踪技术——Cookie与Session,以及过滤器(Filter)和监听器(Listener)的概念和应用。Cookie通过在客户端记录信息来识别用户,而Session则在服务器端保存用户状态。过滤器用于拦截和处理请求及响应,监听器则监控域对象的状态变化。文章详细解释了这些技术的实现方式、应用场景和主要方法,帮助开发者更好地理解和使用这些工具。
315 1
|
存储 算法 Java
java制作海报六:Graphics2D的RenderingHints方法参数详解,包括解决文字不清晰,抗锯齿问题
这篇文章是关于如何在Java中使用Graphics2D的RenderingHints方法来提高海报制作的图像质量和文字清晰度,包括抗锯齿和解决文字不清晰问题的技术详解。
797 0
java制作海报六:Graphics2D的RenderingHints方法参数详解,包括解决文字不清晰,抗锯齿问题
|
Java
java构造方法时对象初始化,实例化,参数赋值
java构造方法时对象初始化,实例化,参数赋值
472 1
|
Java
Java的aop是如何实现的?原理是什么?
Java的aop是如何实现的?原理是什么?
249 4
在Java中定义一个不做事且没有参数的构造方法的作用
Java程序在执行子类的构造方法之前,如果没有用super()来调用父类特定的构造方法,则会调用父类中“没有参数的构造方法”。因此,如果父类中只定义了有参数的构造方法,而在子类的构造方法中又没有用super()来调用父类中特定的构造方法,则编译时将发生错误,因为Java程序在父类中找不到没有参数的构造方法可供执行。解决办法是在父类里加上一个不做事且没有参数的构造方法。
|
安全 Java 测试技术
Java开发必读,谈谈对Spring IOC与AOP的理解
Spring的IOC和AOP机制通过依赖注入和横切关注点的分离,大大提高了代码的模块化和可维护性。IOC使得对象的创建和管理变得灵活可控,降低了对象之间的耦合度;AOP则通过动态代理机制实现了横切关注点的集中管理,减少了重复代码。理解和掌握这两个核心概念,是高效使用Spring框架的关键。希望本文对你深入理解Spring的IOC和AOP有所帮助。
805 0