java反射
参考javassist使用全解析 - rickiyang - 博客园 (cnblogs.com)
还有b站白日梦组长的视频
环境搭建
pom.xml
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.25.0-GA</version>
</dependency>
定义
对于任意一个类,都能够得到这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为 java 语言的反射机制。
其实在 Java 中定义的一个类本身也是一个对象,即 java.lang.Class 类的实例,这个实例称为类对象
类对象表示正在运行的 Java 应用程序中的类和接口
类对象没有公共构造方法,由 Java 虚拟机自动构造 类对象用于提供类本身的信息,比如有几种构造方法, 有多少属性,有哪些普通方法 要得到类的方法和属性,首先就要得到该类对象
假设类对象如下:
public class persons {
public String name;
private int age;
public persons(){
System.out.println("无参构造方法");
}
public persons(String name, int age){
this.name = name;
this.age = age;
}
public void action(String string){
System.out.println(string);
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
获取类对象
此时要获取它的类对象有如下方法:
Class.forName("reflaction.User")
User.class
new User().getClass()
最常见的是第一种,我们康康
意思是传递一个类名返回其 Class 泛型
调用无参构造方法创建对象
当调用无参构造方法的时候,直接 newInstance 即可
public class reflac
{
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException
{
Class persons = Class.forName("persons");
persons.newInstance();
}
}
调用有参构造方法创建对象
常用方法介绍
方法 | 说明 |
---|---|
getConstructor(Class... parameterTypes) | 获得该类中与参数类型匹配的公有构造方法 |
getConstructors() | 获得该类的所有公有构造方法 |
getDeclaredConstructor(Class... parameterTypes) | 获得该类中与参数类型匹配的构造方法 |
getDeclaredConstructors() | 获得该类所有构造方法 |
试着写一下
import java.beans.PersistenceDelegate;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class reflac
{
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException
{
Class persons = Class.forName("persons");
Constructor constructor = persons.getConstructor(String.class, int.class);
Object thai = constructor.newInstance("thai", 20);
System.out.println(thai);
}
}
以后可以照着上面的模板了
其实就是 先初始化constructor,再用其newInstance(参数);
通过反射调用方法
通过反射调用方法 | 方法说明 |
---|---|
getMethod(String name, Class... parameterTypes) | 获得该类某个公有的方法 |
getMethods() | 获得该类所有公有的方法 |
getDeclaredMethod(String name, Class... parameterTypes) | 获得该类某个方法 |
getDeclaredMethods() | 获得该类所有方法 |
在上面的“调用方法”后加上invoke才能使用
import java.beans.PersistenceDelegate;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class reflac
{
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException
{
Class persons = Class.forName("persons");
Constructor constructor = persons.getConstructor(String.class, int.class);
Object thai = constructor.newInstance("thai", 20);
//System.out.println(thai);
Method action = persons.getMethod("action", String.class);
action.invoke(thai,"thaii yyds");
}
}
当然你要两句写成一句也可以
不推荐,因为反序列化利用链里面的invoke往往是分开的
通过反射访问属性
方法 | 说明 |
---|---|
getField(String name) | 获得某个公有的属性对象 |
getFields() | 获得所有公有的属性对象 |
getDeclaredField(String name) | 获得某个属性对 |
getDeclaredFields() | 获得所有属性对象 |
获取属性,赋值
Field name = persons.getDeclaredField("name");
name.set(thai,"thai admire miku233");
private属性要加上 (public也可以加,建议都加吧,这样就不用担心那么多)
field.setAccessible(true);
demo
import java.beans.PersistenceDelegate;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class reflac
{
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException
{
Class persons = Class.forName("persons");
Constructor constructor = persons.getConstructor(String.class, int.class);
Object thai = constructor.newInstance("thai", 20);
System.out.println(thai);
Field name = persons.getDeclaredField("name");
name.set(thai,"thai admire miku233");
//System.out.println(thai);
Field name1 = persons.getDeclaredField("age");
name1.setAccessible(true);
name1.set(thai,21);
System.out.println(thai);
}
}
可以看这个当作
也有一种Classpool的反射的方法
package com.rickiyang.learn.javassist;
import javassist.*;
/**
* @author rickiyang
* @date 2019-08-06
* @Desc
*/
public class CreatePerson {
/**
* 创建一个Person 对象
*
* @throws Exception
*/
public static void createPseson() throws Exception {
ClassPool pool = ClassPool.getDefault();
// 1. 创建一个空类
CtClass cc = pool.makeClass("com.rickiyang.learn.javassist.Person");
// 2. 新增一个字段 private String name;
// 字段名为name
CtField param = new CtField(pool.get("java.lang.String"), "name", cc);
// 访问级别是 private
param.setModifiers(Modifier.PRIVATE);
// 初始值是 "xiaoming"
cc.addField(param, CtField.Initializer.constant("xiaoming"));
// 3. 生成 getter、setter 方法
cc.addMethod(CtNewMethod.setter("setName", param));
cc.addMethod(CtNewMethod.getter("getName", param));
// 4. 添加无参的构造函数
CtConstructor cons = new CtConstructor(new CtClass[]{}, cc);
cons.setBody("{name = \"xiaohong\";}");
cc.addConstructor(cons);
// 5. 添加有参的构造函数
cons = new CtConstructor(new CtClass[]{pool.get("java.lang.String")}, cc);
// $0=this / $1,$2,$3... 代表方法参数
cons.setBody("{$0.name = $1;}");
cc.addConstructor(cons);
// 6. 创建一个名为printName方法,无参数,无返回值,输出name值
CtMethod ctMethod = new CtMethod(CtClass.voidType, "printName", new CtClass[]{}, cc);
ctMethod.setModifiers(Modifier.PUBLIC);
ctMethod.setBody("{System.out.println(name);}");
cc.addMethod(ctMethod);
//这里会将这个创建的类对象编译为.class文件
cc.writeFile("/Users/yangyue/workspace/springboot-learn/java-agent/src/main/java/");
}
public static void main(String[] args) {
try {
createPseson();
} catch (Exception e) {
e.printStackTrace();
}
}
}