private static native Object newInstance0(Constructor> var0, Object[] var1);复制代码
1.2、构造⽅法依赖注⼊时的反射
// 通过反射获取当前类所有的构造⽅法信息(Constructor 对象)
Constructor>[] candidates = beanClass.getDeclaredConstructors();// 设置构造⽅法参数实例
Object[] argsToUse = new Object[parameterTypes.length];argsToUse[i] = getBean(beanNames.get(i));
// 使⽤带有参数的 Constructor 对象实现实例化 Bean。此时使⽤反射跟上⾯⼀样(newInstance0),只是多了参数return constructorToUse.newInstance(argsToUse);复制代码
1.3、setter() ⽅法依赖注⼊时的反射
// 通过反射获取当前类所有的⽅法信息(Method 对象)
Method[] methods = bean.getClass().getDeclaredMethods();// 获得⽅法参数实例
Object propertyBean = getBean(propertyName);
// 通过反射执⾏调⽤ setter() ⽅法。invoke:调⽤⽅法,propertyBean 作为⽅法的参数method.invoke(bean, propertyBean);复制代码
bean.getClass().getDeclaredMethods(); 中的核⼼代码:
// java.lang.Class.java
// 调⽤ native ⽅法,publicOnly 为 falsegetDeclaredMethods0(publicOnly);
// native ⽅法,从 jvm 中的 class ⽂件中获取⽅法信息,再转换为 Methodprivate native Method[] getDeclaredMethods0(boolean publicOnly);复制代码
method.invoke(bean, propertyBean); 中的核⼼代码:
// sun.reflect.NativeMethodAccessorImpl.java
// 调⽤ native ⽅法,var1: bean、var2: propertyBeanreturn invoke0(this.method, var1, var2);
// native ⽅法,运⾏ class ⽂件中的字节码指令
private static native Object invoke0(Method var0, Object var1, Object[] var2);复制代码
1.4、@Autowired 依赖注⼊时的反射
// 通过反射得到当前类所有的字段信息(Field 对象)Field[] fields = bean.getClass().getDeclaredFields();// 判断字段是否有 @Autowired 注解
Annotation ann = field.getAnnotation(Autowired.class);
// 设置字段可连接,相当于将⾮ public(private、default、protect)更改为 publicfield.setAccessible(true);// 通过反射设置字段的值
field.set(bean, getBean(field.getName()));复制代码
bean.getClass().getDeclaredFields(); 中的核⼼代码:
// java.lang.Class.java
// 调⽤ native ⽅法,此时 publicOnly 为 falsegetDeclaredFields0(publicOnly);
// native ⽅法,从 jvm 中获取 class ⽂件的字段信息,再转换为 Fieldprivate native Field[] getDeclaredFields0(boolean publicOnly);复制代码
field.set(bean, getBean(field.getName())); 中的核⼼代码:
// sun.reflect.UnsafeObjectFieldAccessorImpl.java
// 调⽤ native ⽅法,将⽬标对象 var1 指定偏移量 fieldOffset 处的字段值设置(修改)为 var2。var1 为 bean, var2 为参数实例unsafe.putObject(var1, this.fieldOffset, var2);
// sun.misc.Unsafe.java
// native ⽅法,直接修改堆中对象字段的数据
public native void putObject(Object var1, long var2, Object var4);复制代码
⼆、class ⽂件与类对象
class ⽂件由 java ⽂件编译⽽来,class ⽂件包含字段表、⽅法表、 ⽅法(构造⽅法)等。当类加载器将 class ⽂件加载进虚拟机元数据区(⽅法区,jdk1.7)时,虚拟机创建⼀个与之对应的类对象(Class 实例)。并将 class ⽂件由存放在磁盘的静态结构转换为存放在内存的运⾏时结构。
我们可以认为⼀个类(class ⽂件)对应⼀个类对象,当前类的所有对象共⽤⼀个类对象。类对象作为访问存放在 jvm 的 class ⽂件的⼊⼝。
package java.lang;
import java.lang.reflect.Field;import java.lang.reflect.Method;import java.lang.reflect.Constructor;
public final class Class {private native Field[] getDeclaredFields0(boolean publicOnly); private native Method[] getDeclaredMethods0(boolean publicOnly);
private native Constructor[] getDeclaredConstructors0(boolean publicOnly); // ReflectionData 缓存反射对象private static class ReflectionData { volatile Field[] declaredFields; volatile Field[] publicFields;volatile Method[] declaredMethods; volatile Method[] publicMethods;
volatile Constructor[] declaredConstructors; volatile Constructor[] publicConstructors; ... }}复制代码
2.1、获得类对象的⽅式
// 1、通过对象
Class cls = object.getClass();// Object.java
public final native Class> getClass();
// 2、通过类加载器
Class cls = Thread.currentThread().getContextClassLoader().loadClass(\"org.deppwang.litespring.v1.service.PetStoreService\");// 3、通过 Class 类,本质上也是通过类加载器
Class cls = Class.forName(\"org.deppwang.litespring.v1.service.PetStoreService\");// Class.java
private static native Class> forName0(String name, boolean initialize, ClassLoader loader, Class> caller)复制代码
三、反射⽅法
以下是常⽤的反射⽅法。
3.1、Feild 相关
Field[] fields = cls.getFields(); // 获取所有公共的 Field(包括⽗类)
Field[] fields = cls.getDeclaredFields(); // 获取当前类的所有 Field(不包括⽗类),包括公共和⾮公共Field field = cls.getDeclaredField(\"fieldName\"); // 指定获取当前类某个 Fieldfield.set(Object, Object); // 设置(修改)字段值field.get(Object); // 获取字段值复制代码
field.get(Object) 核⼼代码:
// 调⽤ native ⽅法,获取字段对应的值
return unsafe.getObject(var1, this.fieldOffset);// native ⽅法,从堆中获取对象指定位置的对象
public native Object getObject(Object var1, long var2);复制代码
3.2、Method 相关
Method[] methods = cls.getMethods(); // 获取所有公共的 Method(包括⽗类)
Method[] methods = cls.getDeclaredMethods(); // 获取当前类的所有 Method(不包括⽗类),包括公共和⾮公共method.invoke(Object instance, Object... parameters); // 运⾏⽅法复制代码
运⾏⽅法使⽤场景:要么是修改对象的数据,如 void setter() ⽅法;要么是获得执⾏⽅法的返回结果。
String result = method.invoke().toString();复制代码
3.3、Constructor 相关
Constructor>[] constructors = cls.getConstructors(); // 获取所有公共的 Constructor(包括⽗类)
Constructor>[] constructors = cls.getDeclaredConstructors(); // 获取当前类的所有Constructor(不包括⽗类),包括公共和⾮公共constructor.newInstance(Object... parameters); // 运⾏构造⽅法复制代码
当没有明确编写构造⽅法,Java 编译器将为该类构建⼀个默认构造函数 四、native ⽅法
Java 1.1 新增「Java 本地接⼝」(Java Native Interface,JNI),JNI 是⼀种包容极⼴的编程接⼝,允许我们从 Java 应⽤程序⾥调⽤ native ⽅法,native ⽅法由其它语⾔(C 、C++ 或汇编语⾔等)编写。native ⽅法⽤于实现 Java ⽆法处理的功能。
4.1、简单⽰例
⼀个在 Java 中使⽤ Java 本地接⼝(JNI)的简单⽰例。
环境:jdk8、macOS 10.15
// Main.java
public class Main {
public native int intMethod(int i); static {
// 启动时载⼊ libMain.dylib System.loadLibrary(\"Main\"); }
public static void main(String[] args) {
System.out.println(new Main().intMethod(2)); }}
复制代码// Main.c:
// 将 Main.h 引⼊#include \"Main.h\"
// 相当于继承 \"Main.h\" 的 Java_Main_intMethodJNIEXPORT jint JNICALL Java_Main_intMethod( JNIEnv *env, jobject obj, jint i){
return i * i;}
复制代码
编译与运⾏:
// 同时⽣成 Main.class 和 Main.hjavac Main.java -h .
// 根据 Main.c ⽣成 libMain.dylibgcc -dynamiclib -O3 \\ -I/usr/include \\
-I$JAVA_HOME/include \\
-I$JAVA_HOME/include/darwin \\ Main.c -o libMain.dylib
// 指定 library 的路径为当前路径
java -cp . -Djava.library.path=$(pwd) Main复制代码
输出:
4
复制代码
/* Main.h .h 作为头⽂件*/
/* DO NOT EDIT THIS FILE - it is machine generated */#include /* Header for class Main */
#ifndef _Included_Main#define _Included_Main#ifdef __cplusplusextern \"C\" {#endif/*
* Class: Main
* Method: intMethod * Signature: (I)I */
JNIEXPORT jint JNICALL Java_Main_intMethod (JNIEnv *, jobject, jint);#ifdef __cplusplus}
#endif#endif复制代码
javac Main.java -h .// 可拆分为两个命令javac Main.javajavah -jni Main复制代码
4.2、原理
运⾏ Main.class 时,将 libMain.dylib 载⼊虚拟机,JVM 调⽤ libMain.dylib 的 Java_Main_intMethod,传⼊参数,libMain.dylib 由系统直接运⾏,返回结果。
*env ⽤于将 java 类型数据与本地(此处为 C 语⾔)类型数据之间的转换
jint 还是 Java 数据类型,Java 基本数据类型可以映射(使⽤),不⽤通过 *env 转换
/*C code*/
JNIEXPORT void JNICALL Java_ClassName_MethodName (JNIEnv *env, jobject obj, jstring javaString){
/*Get the native string from javaString*/
const char *nativeString = (*env)->GetStringUTFChars(env, javaString, 0); /*Do something with the nativeString*/
/*DON'T FORGET THIS LINE*/
(*env)->ReleaseStringUTFChars(env, javaString, nativeString);}
复制代码
4.3、参考
五、总结
反射反射,哪⾥体现反射字⾯意思?
可以这么理解,通过 native ⽅法得到反射对象,操作反射对象,像镜⼦⼀样,将反射到原对象上。我们发现,反射和 native ⽅法的关系:
获取字段、⽅法、构造⽅法对象,native() ⽅法实现获取字段值、设置修改字段值,native() ⽅法实现运⾏⽅法,native() ⽅法实现运⾏构造⽅法,native() ⽅法实现我们可以得出结论,反射由 native ⽅法实现。我们说通过反射实现⼀个功能,我们也可以说
通过反射⽅法实现通过反射 API 实现通过 native ⽅法实现
反射是⼀种⾮常规(native ⽅法实现)⽅式获取 class ⽂件信息、运⾏ class ⽂件字节码指令和操作对象数据的能⼒。
⼀句话总结 :反射是⼀种运⾏时获取和修改对象数据的能⼒。
关于运⾏时:Java 是静态语⾔,先编译,后运⾏。编译时不执⾏代码,代码都是运⾏时执⾏。
最后注意:光理论是不够的,记住:Java架构项⽬经验永远是核⼼,如果你没有最新JAVA架构实战教程及⼤⼚30k+⾯试宝典,可以去⼩编的Java架构学习.裙 :七吧伞
吧零⽽⾐零伞 (数字的谐⾳)转换下可以找到了,⾥⾯很多新JAVA架构项⽬教程,还可以跟⽼司机交流讨教!
本⽂的⽂字及图⽚来源于⽹络加上⾃⼰的想法,仅供学习、交流使⽤,不具有任何商业⽤途,版权归原作者所有,如有问题请及时联系我们以作处理