Java 反射
定义
Java的反射(reflection)机制是指在程序的运行状态
中,可以构造任意一个类
的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。
简而言之,反射机制可以获取任意一个类的字节码,并通过这个字节码文件创建出类对象,访问该对象。
关键词:任意,可见Java的反射机制是非常灵活的,在spring框架中得到了充分的应用 - 配置文件和类的自动装配,即 IOC 机制。
如何反射?
要使用反射机制反射出一个类,那么必须先获得类的字节码文件对象,即 .class。
如何获取类的字节码文件?有几种方式?
-
调用某个类的对象实例的 getClass() 方法获取
Cat cat = new Cat(); Class c = cat.getClass();
-
直接调用类的 .class 属性
Class c = Cat.class();
-
使用 Class 的 forName(String className) 方法,通过类的全路径名
Class c = Class.forName("reflect.Cat"); reflect.Cat 是 Cat 类的全路径名
由于第三种方式不需要事先创建类,或者获取类,所以更灵活。 Spring 就是用这样的方式进行组件的创建,根据配置文件和反射机制和IOC创建、获取、装配组件。
使用场景
1、通过反射机制破坏单例模式
单例模式中,类的构造方法都是设置为 private 的,所以使用常规的 new 是无法创建对象的。但是,反射机制可以。
做法:我们可以先获取类的字节码文件,然后通过字节码文件获取类的构造器,将构造器可访问权限设置为 true,调用构造器的 newInstance() 获取一个对象实例。
class Cat {
String name;
private Cat() {}
}
public class ReflectTest {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, ClassNotFoundException {
Class<Cat> catClass = Cat.class;
Constructor<Cat> cata = catClass.getDeclaredConstructor();
cata.setAccessible(true);
Cat cat1 = cata.newInstance();
Cat cat2 = Cat.getCat();
System.out.println(cat1);
System.out.println(cat2);
}
}
输出:
得到两个不同的对象!
2、Spring的IOC机制中的应用
Spring 是如何帮我们创建对象,并放到容器中?IOC 机制
实际上 IOC机制实际上就是使用了反射机制来实现的。
我们看下,下面的伪代码实现过程:
//解析<bean .../>元素的id属性得到该字符串值为"sqlSessionFactory"
String idStr = "sqlSessionFactory";
//解析<bean .../>元素的class属性得到该字符串值为"org.mybatis.spring.SqlSessionFactoryBean"
String classStr = "org.mybatis.spring.SqlSessionFactoryBean";
//利用反射知识,通过classStr获取Class类对象
Class cls = Class.forName(classStr);
//实例化对象
Object obj = cls.newInstance();
//container表示Spring容器
container.put(idStr, obj);
//当一个类里面需要用另一类的对象时,我们继续下面的操作
//解析<property .../>元素的name属性得到该字符串值为“dataSource”
String nameStr = "dataSource";
//解析<property .../>元素的ref属性得到该字符串值为“dataSource”
String refStr = "dataSource";
//生成将要调用setter方法名
String setterName = "set" + nameStr.substring(0, 1).toUpperCase() + nameStr.substring(1);
//获取spring容器中名为refStr的Bean,该Bean将会作为传入参数
Object paramBean = container.get(refStr);
//获取setter方法的Method类,此处的cls是刚才反射代码得到的Class对象
Method setter = cls.getMethod(setterName, paramBean.getClass());
//调用invoke()方法,此处的obj是刚才反射代码得到的Object对象
setter.invoke(obj, paramBean);
是不是很熟悉,虽然是伪代码,但是和我们本篇讲的反射机制的使用是相同的,现在知道我们的反射机制用在哪了吧,没错就是我们经常提到的Java web框架中,里面就用到了反射机制,只要在代码或配置文件中看到类的完全限定名(包名+类名),即完整路径名,其底层原理基本上使用的就是Java的反射机制。
ref:
- https://blog.csdn.net/Appleyk/article/details/77879073
- https://codeantenna.com/a/USAuldhCFd