反射之所以被称为框架的灵魂,是因为它赋予了我们在运行时分析类以及执行类中方法的能力。
通过反射你可以获取任意一个类的所有属性和方法,你还可以调用这些方法和属性。
一.反射的应用场景?
像 Spring/Spring Boot、MyBatis 等等框架中都大量使用了反射机制。
这些框架中也大量使用了动态代理,而动态代理的实现也依赖反射。
1.加载数据库驱动
//1.DriverManager
DriverManager.registerDriver(new com.mysql.cj.jdbc.Driver());
//2.Class.forName
Class.forName("com.mysql.cj.jdbc.Driver");
2.xml或properties等配置文件加载
Spring通过XML配置模式装载Bean的过程
- 将程序中所有XML或properties配置文件加载入内存
- Java类里面解析xml或者properties里面的内容,得到对应实体类的字节码字符串以及相关的属性信息
- 使用反射机制,根据这个字符串获得某个类的Class实例
- 动态配置实例的属性
配置文件
className=com.example.reflectdemo.TestInvokemethodName=printlnState
实体类
public class TestInvoke {
private void printlnState(){
System.out.println("I am fine");
}
}
?解析配置文件内容
// 解析xml或properties里面的内容,得到对应实体类的字节码字符串以及属性信息
public static String getName(String key) throws IOException {
Properties properties = new Properties();
FileInputStream in = new FileInputStream("D:\IdeaProjects\AllDemos\language-specification\src\main\resources\application.properties");
properties.load(in);
in.close();
return properties.getProperty(key);
}
利用反射获取实体类的Class实例,创建实体类的实例对象,调用方法
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, IOException, ClassNotFoundException, InstantiationException {
// 使用反射机制,根据这个字符串获得Class对象
Class<?> c = Class.forName(getName("className"));
System.out.println(c.getSimpleName());
// 获取方法
Method method = c.getDeclaredMethod(getName("methodName"));
// 绕过安全检查
method.setAccessible(true);
// 创建实例对象
TestInvoke testInvoke = (TestInvoke)c.newInstance();
// 调用方法
method.invoke(testInvoke);}
二.反射机制的优缺点
优点?: 可以让咱们的代码更加灵活、为各种框架提供开箱即用的功能提供了便利
缺点?:让我们在运行时有了分析操作类的能力,这同样也增加了安全问题。比如可以无视泛型参数的安全检查(泛型参数的安全检查发生在编译时)。另外,反射的性能也要稍差点,不过,对于框架来说实际是影响不大的
三.获取 Class 对象的四种方式
Java 提供了四种方式获取 Class 对象:
1. 知道具体类的情况下可以使用:
Class targetClass = TargetObject.class;
但是我们一般是不知道具体类的,基本都是通过遍历包下面的类来获取 Class 对象,通过此方式获取 Class 对象不会进行初始化
2. 通过?Class.forName() 传入类的全路径获取:
Class targetClass = Class.forName("com.lzm.TargetObject");
3. 通过对象实例instance.getClass() 获取:
TargetObject o = new TargetObject();
Class targetClass = o.getClass();
4. 通过类加载器xxxClassLoader.loadClass() 传入类路径获取:
Class clazz = ClassLoader.loadClass("com.lzm.TargetObject");
通过类加载器获取 Class 对象不会进行初始化,意味着不进行包括初始化等一系列步骤,静态代码块和静态对象不会得到执行
四.反射的一些基本操作
1.创建一个我们要使用反射操作的类?TargetObject
?
package com.lzm;
public class TargetObject {
private String value;
public TargetObject(){
value="lzm";
}
public void publicMethod(String s){
System.out.println("I am "+s);
}
private void privateMethod(){
System.out.println("value is "+value);
}
}
2.使用反射操作这个类的方法以及参数
???????
package com.lzm;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Reflection {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
/**
* 获取TargetObject类的Class对象并且创建TargetObject类实例
*/
Class<?> targetClass=Class.forName("com.lzm.TargetObject");
TargetObject targetObject= (TargetObject) targetClass.newInstance();
/**
* 获取TargetObject类中定义的所有方法
*/
Method[] methods=targetClass.getDeclaredMethods();
for(Method method:methods){
System.out.println(method.getName());
}
/**
* 获取指定方法并调用
*/
Method publicMethod=targetClass.getDeclaredMethod("publicMethod", String.class);
publicMethod.invoke(targetObject,"lzmzcc");
/**
* 获取指定参数并对参数进行修改
*/
Field field = targetClass.getDeclaredField("value");
//为了对类中的参数进行修改,所以取消安全检查
field.setAccessible(true);
field.set(targetObject,"lzmzcc");
/**
* 调用private方法
*/
Method privateMethod = targetClass.getDeclaredMethod("privateMethod");
privateMethod.setAccessible(true);
privateMethod.invoke(targetObject);
}
}
输出内容:
?
|