反射の定義
1.JAVAリフレクションメカニズムは実行状態です
どのクラスでも、このクラスのすべての属性とメソッドを知ることができます。
任意のオブジェクトについて、そのメソッドとプロパティのいずれかを呼び出すことができます。
情報を動的に取得し、オブジェクトのメソッドを動的に呼び出す機能は、Java言語のリフレクションメカニズムと呼ばれます。
リフレクションによって提供される機能:
- 実行時に任意のオブジェクトのクラスを決定します
- 実行時に任意のクラスのオブジェクトを作成します
- 実行時に任意のクラスのメンバー変数とメソッドを判断します
- 実行時に任意のオブジェクトのメソッドを呼び出す
(属性がプライベートの場合、通常の状況では外部が属性値を操作することはできません。ここでは、FieldクラスのsetAccessible(true)メソッドを使用して、操作権限を一時的に開くことができます)
振り返りのシナリオを使用する
- Javaでコーディングするときは、クラスとオブジェクトの特定の情報を知ってください。現時点では、反映せずにクラスとオブジェクトを直接操作できます。
- コーディング時にクラスまたはオブジェクトの特定の情報がわからない場合は、リフレクションを使用して達成する必要があります
リフレクションソースコード分析
APIの例:
Class.forName("com.my.reflectTest").newInstance()
1.リフレクトしてクラスインスタンスClass.forName( "xxx");を取得します。
まず、java.lang.Classの静的メソッドを呼び出して、クラス情報を取得します。
注:リフレクションによってクラス情報を取得するforName()は、実装をjavaに任せませんが、jvmに任せてロードします!
主なことは、最初にClassLoaderを取得し、次にネイティブメソッドを呼び出して情報を取得することです。ロードするクラスは、パラメーターClassLoaderを呼び出して、クラスをロードすることです。
@CallerSensitive
public static Class<?> forName(String className)
throws ClassNotFoundException {
// 先通过反射,获取调用进来的类信息,从而获取当前的 classLoader
Class<?> caller = Reflection.getCallerClass();
// 调用native方法进行获取class信息
return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}
2. java.lang.ClassLoader ----- loadClass()
// java.lang.ClassLoader
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
// 先获取锁
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
// 如果已经加载了的话,就不用再加载了
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
// 双亲委托加载
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
// 父类没有加载到时,再自己加载
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
c = findClass(name);
// this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
protected Object getClassLoadingLock(String className) {
Object lock = this;
if (parallelLockMap != null) {
// 使用 ConcurrentHashMap来保存锁
Object newLock = new Object();
lock = parallelLockMap.putIfAbsent(className, newLock);
if (lock == null) {
lock = newLock;
}
}
return lock;
}
protected final Class<?> findLoadedClass(String name) {
if (!checkName(name))
return null;
return findLoadedClass0(name);
}
3. newInstance()
newInstance() 其实相当于调用类的无参构造函数,主要做了三件事
-
権限検出が渡されない場合、例外を直接スローします。
-
パラメータのないコンストラクタを見つけてキャッシュします。
- 特定のメソッドのパラメーターなしの構築メソッドを呼び出し、インスタンスを生成して返します。
// 首先肯定是 Class.newInstance
@CallerSensitive
public T newInstance()
throws InstantiationException, IllegalAccessException
{
if (System.getSecurityManager() != null) {
checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), false);
}
// NOTE: the following code may not be strictly correct under
// the current Java memory model.
// Constructor lookup
// newInstance() 其实相当于调用类的无参构造函数,所以,首先要找到其无参构造器
if (cachedConstructor == null) {
if (this == Class.class) {
// 不允许调用 Class 的 newInstance() 方法
throw new IllegalAccessException(
"Can not call newInstance() on the Class for java.lang.Class"
);
}
try {
// 获取无参构造器
Class<?>[] empty = {};
final Constructor<T> c = getConstructor0(empty, Member.DECLARED);
// Disable accessibility checks on the constructor
// since we have to do the security check here anyway
// (the stack depth is wrong for the Constructor's
// security check to work)
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<Void>() {
public Void run() {
c.setAccessible(true);
return null;
}
});
cachedConstructor = c;
} catch (NoSuchMethodException e) {
throw (InstantiationException)
new InstantiationException(getName()).initCause(e);
}
}
Constructor<T> tmpConstructor = cachedConstructor;
// Security check (same as in java.lang.reflect.Constructor)
int modifiers = tmpConstructor.getModifiers();
if (!Reflection.quickCheckMemberAccess(this, modifiers)) {
Class<?> caller = Reflection.getCallerClass();
if (newInstanceCallerCache != caller) {
Reflection.ensureMemberAccess(caller, this, null, modifiers);
newInstanceCallerCache = caller;
}
}
// Run constructor
try {
// 调用无参构造器
return tmpConstructor.newInstance((Object[])null);
} catch (InvocationTargetException e) {
Unsafe.getUnsafe().throwException(e.getTargetException());
// Not reached
return null;
}
}
4. getConstructor0()は、一致するコンストラクターを取得するためのものです。次の3つのステップがあります。
1.最初にすべてのコンストラクターを取得し、次にパラメータータイプを比較します
。2。一致するものを見つけ
たら、ReflectionFactoryを介してコンストラクターのコピーをコピーして返します。3。それ以外の場合は、NoSuchMethodExceptionをスローします。
private Constructor<T> getConstructor0(Class<?>[] parameterTypes,
int which) throws NoSuchMethodException
{
// 获取所有构造器
Constructor<T>[] constructors = privateGetDeclaredConstructors((which == Member.PUBLIC));
for (Constructor<T> constructor : constructors) {
if (arrayContentsEq(parameterTypes,
constructor.getParameterTypes())) {
return getReflectionFactory().copyConstructor(constructor);
}
}
throw new NoSuchMethodException(getName() + ".<init>" + argumentTypesToString(parameterTypes));
}
5. privateGetDeclaredConstructors()、すべてのコンストラクターを取得するための主な手順。
1.最初にキャッシュから取得してみてください
。2。キャッシュが使用できない場合は、jvmから再度取得して、キャッシュに保存します。メモリが使用可能であることを確認するために、キャッシュはソフトリファレンスとともに保存されます。
// 获取当前类所有的构造方法,通过jvm或者缓存
// Returns an array of "root" constructors. These Constructor
// objects must NOT be propagated to the outside world, but must
// instead be copied via ReflectionFactory.copyConstructor.
private Constructor<T>[] privateGetDeclaredConstructors(boolean publicOnly) {
checkInitted();
Constructor<T>[] res;
// 调用 reflectionData(), 获取保存的信息,使用软引用保存,从而使内存不够可以回收
ReflectionData<T> rd = reflectionData();
if (rd != null) {
res = publicOnly ? rd.publicConstructors : rd.declaredConstructors;
// 存在缓存,则直接返回
if (res != null) return res;
}
// No cached value available; request value from VM
if (isInterface()) {
@SuppressWarnings("unchecked")
Constructor<T>[] temporaryRes = (Constructor<T>[]) new Constructor<?>[0];
res = temporaryRes;
} else {
// 使用native方法从jvm获取构造器
res = getDeclaredConstructors0(publicOnly);
}
if (rd != null) {
// 最后,将从jvm中读取的内容,存入缓存
if (publicOnly) {
rd.publicConstructors = res;
} else {
rd.declaredConstructors = res;
}
}
return res;
}
// Lazily create and cache ReflectionData
private ReflectionData<T> reflectionData() {
SoftReference<ReflectionData<T>> reflectionData = this.reflectionData;
int cla***edefinedCount = this.cla***edefinedCount;
ReflectionData<T> rd;
if (useCaches &&
reflectionData != null &&
(rd = reflectionData.get()) != null &&
rd.redefinedCount == cla***edefinedCount) {
return rd;
}
// else no SoftReference or cleared SoftReference or stale ReflectionData
// -> create and replace new instance
return newReflectionData(reflectionData, cla***edefinedCount);
}
// 新创建缓存,保存反射信息
private ReflectionData<T> newReflectionData(SoftReference<ReflectionData<T>> oldReflectionData,
int cla***edefinedCount) {
if (!useCaches) return null;
// 使用cas保证更新的线程安全性,所以反射是保证线程安全的
while (true) {
ReflectionData<T> rd = new ReflectionData<>(cla***edefinedCount);
// try to CAS it...
if (Atomic.casReflectionData(this, oldReflectionData, new SoftReference<>(rd))) {
return rd;
}
// 先使用CAS更新,如果更新成功,则立即返回,否则测查当前已被其他线程更新的情况,如果和自己想要更新的状态一致,则也算是成功了
oldReflectionData = this.reflectionData;
cla***edefinedCount = this.cla***edefinedCount;
if (oldReflectionData != null &&
(rd = oldReflectionData.get()) != null &&
rd.redefinedCount == cla***edefinedCount) {
return rd;
}
}
}
さらに、キャッシュストレージにはrelactionData()を使用します。ReflectionDataのデータ構造は次のとおりです。
// reflection data that might get invalidated when JVM TI RedefineClasses() is called
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;
// Intermediate results for getFields and getMethods
volatile Field[] declaredPublicFields;
volatile Method[] declaredPublicMethods;
volatile Class<?>[] interfaces;
// Value of cla***edefinedCount when we created this ReflectionData instance
final int redefinedCount;
ReflectionData(int redefinedCount) {
this.redefinedCount = redefinedCount;
}
}
6.上記により、コンストラクターが取得されます。次に、対応するコンストラクターのnewInstance()を呼び出して、インスタンスを返します。
// return tmpConstructor.newInstance((Object[])null);
// java.lang.reflect.Constructor
@CallerSensitive
public T newInstance(Object ... initargs)
throws InstantiationException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException
{
if (!override) {
if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
Class<?> caller = Reflection.getCallerClass();
checkAccess(caller, clazz, null, modifiers);
}
}
if ((clazz.getModifiers() & Modifier.ENUM) != 0)
throw new IllegalArgumentException("Cannot reflectively create enum objects");
ConstructorAccessor ca = constructorAccessor; // read volatile
if (ca == null) {
ca = acquireConstructorAccessor();
}
@SuppressWarnings("unchecked")
T inst = (T) ca.newInstance(initargs);
return inst;
}
// sun.reflect.DelegatingConstructorAccessorImpl
public Object newInstance(Object[] args)
throws InstantiationException,
IllegalArgumentException,
InvocationTargetException
{
return delegate.newInstance(args);
}
// sun.reflect.NativeConstructorAccessorImpl
public Object newInstance(Object[] args)
throws InstantiationException,
IllegalArgumentException,
InvocationTargetException
{
// We can't inflate a constructor belonging to a vm-anonymous class
// because that kind of class can't be referred to by name, hence can't
// be found from the generated bytecode.
if (++numInvocations > ReflectionFactory.inflationThreshold()
&& !ReflectUtil.isVMAnonymousClass(c.getDeclaringClass())) {
ConstructorAccessorImpl acc = (ConstructorAccessorImpl)
new MethodAccessorGenerator().
generateConstructor(c.getDeclaringClass(),
c.getParameterTypes(),
c.getExceptionTypes(),
c.getModifiers());
parent.setDelegate(acc);
}
// 调用native方法,进行调用 constructor
return newInstance0(c, args);
}
コンストラクターのインスタンスを返した後、外部に応じて型変換を実行できるため、インターフェースまたはメソッドを使用してインスタンス関数を呼び出すことができます。
総括する
パブリックアカウントに注目することを歓迎します:明るい未来、そして一流メーカーからのJavaインタビュー質問の要約+知識ポイントの学習ガイド+ 300ページのpdfドキュメントでのJavaコア知識ポイントの要約を受け取ります!
これらの資料の内容は、インタビュアーがインタビュー中に尋ねなければならないすべての知識ポイントです。この章には、基本的な知識、Javaコレクション、JVM、マルチスレッド同時実行、春の原則、マイクロサービス、NettyおよびRPC、Kafkaなどの多くの知識ポイントが含まれています。 、日記、デザインパターン、Javaアルゴリズム、データベース、Zookeeper、分散キャッシュ、データ構造など。