java中的反射(三)
目录
一、反射
1、class类
2、访问字段
3、调用方法
4、调用构造方法
5、获取继承对象
6、动态代理
二、sping中的反射
本篇转自:https://depp.wang/2020/05/05/reflection-in-spring-and-reflection-principle/
本篇内容
spring中的反射
java中的反射(一):
java中的反射(二):
一、Spring中的反射
1.1、创建 Bean 实例时的反射
// 通过类加载器,根据 class 路径,得到其类对象
Class<?> clz = Thread.currentThread().getContextClassLoader().loadClass("org.deppwang.litespring.v1.service.PetStoreService");
// 根据类对象生成 Bean 实例
return clz.newInstance();
反射体现在 clz.newInstance(); 中,核心代码可分为两部分:
1、利用反射获取当前类 PetStoreService 的所有构造方法信息(Constructor 对象)
// java.lang.Class.java
// 调用 native 方法,此时 publicOnly 为 false
res = getDeclaredConstructors0(publicOnly);
// native 方法,从 jvm 中的 class 文件中获取构造方法信息,再转换为 Constructor 对象
private native Constructor<T>[] getDeclaredConstructors0(boolean publicOnly);
2、利用反射通过默认构造方法生成实例
// sun.reflect.NativeConstructorAccessorImpl.java
// 调用 native 方法,var1 代表构造方法的参数,此时为 null
return newInstance0(this.c, var1);
// native 方法,真正生成实例的方法,执行 class 文件的构造方法 <init>
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 为 false
getDeclaredMethods0(publicOnly);
// native 方法,从 jvm 中的 class 文件中获取方法信息,再转换为 Method
private native Method[] getDeclaredMethods0(boolean publicOnly);
method.invoke(bean, propertyBean); 中的核心代码:
// sun.reflect.NativeMethodAccessorImpl.java
// 调用 native 方法,var1: bean、var2: propertyBean
return 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)更改为 public
field.setAccessible(true);
// 通过反射设置字段的值
field.set(bean, getBean(field.getName()));
bean.getClass().getDeclaredFields(); 中的核心代码:
// java.lang.Class.java
// 调用 native 方法,此时 publicOnly 为 false
getDeclaredFields0(publicOnly);
// native 方法,从 jvm 中获取 class 文件的字段信息,再转换为 Field
private 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 文件加载进虚拟机元空间(Meta-space,jdk 1.8)时,虚拟机在元空间中创建一个与之对应的类对象(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<T> {
private native Field[] getDeclaredFields0(boolean publicOnly);
private native Method[] getDeclaredMethods0(boolean publicOnly);
private native Constructor<T>[] getDeclaredConstructors0(boolean publicOnly);
// ReflectionData 缓存反射对象
private static class ReflectionData<T> {
volatile Field[] declaredFields;
volatile Field[] publicFields;
volatile Method[] declaredMethods;
volatile Method[] publicMethods;
volatile Constructor<T>[] declaredConstructors;
volatile Constructor<T>[] 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"); // 指定获取当前类某个 Field
field.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)的简单示例。
// 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_intMethod
JNIEXPORT jint JNICALL Java_Main_intMethod(
JNIEnv *env, jobject obj, jint i)
{
return i * i;
}
编译与运行:
// 同时生成 Main.class 和 Main.h
javac Main.java -h .
// 根据 Main.c 生成 libMain.dylib
gcc -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 <jni.h>
/* Header for class Main */
#ifndef _Included_Main
#define _Included_Main
#ifdef __cplusplus
extern "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.java
javah -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);
}
总结
反射反射,哪里体现反射字面意思?
可以这么理解,通过 native 方法得到反射对象,操作反射对象,像镜子一样,将反射到原对象上。
我们发现,反射和 native 方法的关系:
获取字段、方法、构造方法对象,native() 方法实现
获取字段值、设置修改字段值,native() 方法实现
运行方法,native() 方法实现
运行构造方法,native() 方法实现
我们可以得出结论,反射由 native 方法实现。
我们说通过反射实现一个功能,我们也可以说:
- 通过反射方法实现
- 通过反射 API 实现
- 通过 native 方法实现

反射是一种非常规(native 方法实现)方式获取 class 文件信息、运行 class 文件字节码指令和操作对象数据的能力。
一句话总结 :反射是一种运行时获取和修改对象数据的能力。
关于运行时:Java 是静态语言,先编译,后运行。编译时不执行代码,代码都是运行时执行。
java中的反射(三)的更多相关文章
- 浅说Java中的反射机制(二)
写过一篇Java中的反射机制,不算是写,应该是抄了,因为那是别人写的,这一篇也是别人写的,摘抄如下: 引自于Java基础--反射机制的知识点梳理,作者醉眼识朦胧.(()为我手记) 什么是反射? 正常编 ...
- 浅说Java中的反射机制(一)
在学习传智播客李勇老师的JDBC系列时,会出现反射的概念,由于又是第一次见,不免感到陌生.所以再次在博客园找到一篇文章,先记录如下: 引用自java中的反射机制,作者bingoideas.(()为我手 ...
- java中动态反射
java中动态反射能达到的效果和python的语法糖很像,能够截获方法的实现,在真实方法调用之前和之后进行修改,甚至能够用自己的实现进行特别的替代,也可以用其实现面向切片的部分功能.动态代理可以方便实 ...
- 第89节:Java中的反射技术
第89节:Java中的反射技术 反射技术是动态的获取指定的类,和动态的调用类中的内容(没有类前就可以创建对象,将对象的动作完成,这就是动态的获取指定的类). 配置文件把具体实现的类名称定义到配置文件中 ...
- 【Java基础】java中的反射机制与动态代理
一.java中的反射机制 java反射的官方定义:在运行状态下,可以获取任意一个类的所有属性和方法,并且可通过某类任意一对象实例调用该类的所有方法.这种动态获取类的信息及动态调用类中方法的功能称为ja ...
- java中的反射(二)
java中的反射(一):https://www.cnblogs.com/KeleLLXin/p/14060555.html 目录 一.反射 1.class类 2.访问字段 3.调用方法 4.调用构造方 ...
- Java中的反射以及简单运用(原理+例子)
Java反射 学习内容 1. 为什么要使用反射 2. 反射的概念 3. Java反射加载过程 4. 字节码对象理解 5. 获取字节码对象(.class)的三种方式 6. 反射常用API 8. 反射综合 ...
- Java中的反射和注解
前言 在Java中,反射机制和注解机制一直是一个很重要的概念,那么他们其中的原理是怎么样呢,我们不仅仅需要会使用,更要知其然而之所以然. 目录 反射机制 反射如何使用 注解定义 注解机制原理 注解如何 ...
- java中的反射机制在Android开发中的用处
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法和属性:这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反 ...
随机推荐
- 虚拟DOM与diff算法
虚拟DOM与diff算法 虚拟DOM 在DOM操作中哪怕我们的数据,发生了一丢丢的变化,也会被强制重建整预DOM树.这么做,涉及到很多元素的重绘和重排,导致性能浪费严重 只要实现按需更新页面上的元素即 ...
- 主动关闭 time-wait 2msl 处理
先上传后面整理 /* * This routine is called by the ICMP module when it gets some * sort of error condition. ...
- gcc入门(下)
一 头文件与库文件(模块化,可重用,好维护)在使用C语言和其他语言进行程序设计的时候,我们需要头文件来提供对常数的定义和对系统以及库函数调用的声明库文件是一些预先编译好的函数的集合,那些函数都是按照可 ...
- Java基础—Java方法的调用
Java方法的调用个主要有以下几种: 1.调用非静态方法 2.调用静态方法 3.方法与方法之间的调用 (1).静态方法内部调用其他方法 (2).非静态方法内部调用 1.调用非静态方法: 非静态方法的调 ...
- python笔记(1)---数据类型
数据类型 基本的五大数据类型 对python中的变量有如下的五种基本的数据类型: Number数字 list列表 Tuple元组 string字符串 Dictionary字典 1.Number [注意 ...
- Springboot整合163邮箱部署项目时发送不了邮件的解决方案
部署到腾讯云服务器时项目报错 Caused by: com.sun.mail.util.MailConnectException: Couldn't connect to host, port: sm ...
- 在CorelDRAW中如何完成属性的复制
复制功能在任何一个编辑软件中都是必不可少.使用率很高的一个功能,在矢量图形设计软件CorelDRAW 中也不例外.关于对象的复制这里就不过多示意了,主要为大家示范一下如何在设计中复制对象的一些属性. ...
- 模拟赛38 B. T形覆盖 大模拟
题目描述 如果玩过俄罗斯方块,应该见过如下图形: 我们称它为一个 \(T\) 形四格拼板 .其中心被标记为\(×\). 小苗画了一个 \(m\) 行 \(n\) 列的长方形网格.行从 \(0\) 至 ...
- 关于iOS路径变化的解决方案
问题描述: 使用沙盒存储文件的时候,我们会保存文件的绝对路劲以便下次读取,但是发现一个现象,我们保存的文件,在第二次打开App去查找的时候,发现找不到了...... 查找原因: iOS8之后,苹果添加 ...
- Java基础教程——字符流
字符流 字节流服务文本文件时,可能出现中文乱码.因为一个中文字符可能占用多个字节. 针对于非英语系的国家和地区,提供了一套方便读写方式--字符流. java.io.Reader java.io.Wri ...