Анатомия метода отражения
Class
Object предоставляет следующие методы для получения объекта (Method
):
getMethod
- Вернуть определенный метод класса или интерфейса, который может быть только общедоступным измененным методом.getDeclaredMethod
- Возвращает определенный метод класса или интерфейса.getMethods
- Возвращает все общедоступные методы класса или интерфейса, включая общедоступные методы родительских классов.getDeclaredMethods
- Возвращает все методы класса или интерфейса, включая общедоступные, защищенные, доступ по умолчанию (пакет) и частные методы, но исключая унаследованные методы.
прецедент
//父类
public class RefFather {
public void refFatherMethod(){
System.out.println("我是父类方法 refFatherMethod");
}
}
//子类
public class RefSon extends RefFather{
private void refSonMethod(){
System.out.println("我是子类方法 refMethod");
}
}
//测试方法
public static void main(String[] args) {
try {
//类的全限定名 即包名+类名
Class<?> clz = Class.forName("main.ref.RefSon");
Object obj = clz.newInstance();
Method refFatherMethod = clz.getMethod("refFatherMethod");
Method refSonMethod = clz.getDeclaredMethod("refSonMethod");
refSonMethod.invoke(obj);
} catch (Exception e) {
e.printStackTrace();
}
}
Method
Первый взглядClass.getMethod(String name, Class<?>... parameterTypes)
Исходный код:
@CallerSensitive
public Method getMethod(String name, Class<?>... parameterTypes)
throws NoSuchMethodException, SecurityException {
checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
Method method = getMethod0(name, parameterTypes, true);
if (method == null) {
throw new NoSuchMethodException(getName() + "." + name + argumentTypesToString(parameterTypes));
}
return method;
}
Приведенный выше исходный код нам нужно только сосредоточиться на решении следующих вопросов, чтобы знать
getMethod
Как реализуется метод.
@CallerSensitive
checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
Reflection.getCallerClass()
Method method = getMethod0(name, parameterTypes, true);
@CallerSensitive
На самом деле эта аннотация используется Java для устранения уязвимости. Запретить пользователям использовать двойное отражение для увеличения разрешений. Принцип заключается в том, что в то время отражение проверяло только наличие разрешений у класса глубокого вызывающего объекта. Изначально у моего собственного класса не было таких высоких разрешений, но я могу использовать множественные отражения для улучшения разрешения на звонки. .
Например, в классе отражения метод должен выяснить, есть ли у вызывающего с двумя фиксированными слоями разрешение (разрешения класса, связанные с отражением, относительно высоки), я могу передать свой собственный класс -> Reflection 1 -> Reflection 2. В таком цепочка вызовов, отражение 2 проверяет разрешения отражения 1, что приводит к уязвимостям безопасности. Используя такие аннотации, затемgetCallerClass
пропустит напрямую@CallerSensitive
Украшенный интерфейсный метод, который напрямую ищет реального вызывающего абонента (actual caller
).
checkMemberAccess
Сначала посмотрите на исходный код этого метода:
private void checkMemberAccess(int which, Class<?> caller, boolean checkProxyInterfaces) {
final SecurityManager s = System.getSecurityManager();
if (s != null) {
final ClassLoader ccl = ClassLoader.getClassLoader(caller);
final ClassLoader cl = getClassLoader0();
if (which != Member.PUBLIC) {
if (ccl != cl) {
s.checkPermission(SecurityConstants.CHECK_MEMBER_ACCESS_PERMISSION);
}
}
this.checkPackageAccess(ccl, checkProxyInterfaces);
}
}
Его функция в основном состоит в том, чтобы проверить, разрешен ли клиенту доступ к элементу.
Политика по умолчанию: разрешить всем клиентам использовать обычный доступ к Java для управления доступом.
Что это значит?
То есть, если вы не будете активно настраиватьSecurityManager
Менеджер безопасности определяет, есть ли у него разрешение в соответствии с модификатором доступа класса Class. Напримерprotected
Разрешен доступ только к одному и тому же пакету или подклассу, а межпакетный доступ становится закрытым и запрещенным.
final ClassLoader ccl = ClassLoader.getClassLoader(caller);
final ClassLoader cl = getClassLoader0();
Если есть обычайSecurityManager
(передачаSystem.setSecurityManager(new MySecurityManager())
), он будет судить, имеют ли вызывающий и посетитель один и тот же загрузчик классов.Если это так, даже если это не модификатор общего доступа, ограничений на разрешения нет. В общем загрузчик классов тутClassLoader
Оба являются загрузчиками классов приложений.Application ClassLoader
.
Вот краткое введениеApplication ClassLoader
:
Этот загрузчик классов отвечает за загрузку библиотеки классов по пользовательскому пути к классу (CLASSPATH). Как правило, классы Java, которые мы пишем, загружаются этим загрузчиком классов. Этот загрузчик классов является возвращаемым значением метода getSystemClassLoader() в CLassLoader, поэтому его также называют системным загрузчиком классов.В общем, это системный загрузчик классов по умолчанию.
Reflection.getCallerClass()
Продолжайте смотреть исходный код:
/** @deprecated */
@Deprecated
public static native Class<?> getCallerClass(int var0);
native означает, что этот метод фактически реализован другими языками, поэтому здесь объясняется только его использование.
Краткое введение
Reflection.getCallerClass(int var0)
, вы можете узнать, если вы заинтересованы.Если значение var0 равно 0 или меньше 0, вернуть
class sun.reflect.Reflection
;Если значение var0 равно 1, он возвращает свой собственный класс. То есть, в каком классе находится эта строка кода, этот класс и возвращается.
Если значение var0 равно 2, возвращается класс, возвращаемый вызывающей программе. Например, этот метод находится в методе A пользователя A, а метод A вызывается в методе B. В настоящее время
Reflection.getCallerClass(int var0)
То, что возвращается, является классом B.
getMethod0(name, parameterTypes, true)
Продолжайте получать OvO:
private Method getMethod0(String name, Class<?>[] parameterTypes, boolean includeStaticMethods) {
MethodArray interfaceCandidates = new MethodArray(2);
Method res = privateGetMethodRecursive(name, parameterTypes, includeStaticMethods, interfaceCandidates);
if (res != null)
return res;
// Not found on class or superclass directly
interfaceCandidates.removeLessSpecifics();
return interfaceCandidates.getFirst(); // may be null
}
private Method privateGetMethodRecursive(String name,
Class<?>[] parameterTypes,
boolean includeStaticMethods,
MethodArray allInterfaceCandidates) {
Method res;
// Search declared public methods
if ((res = searchMethods(privateGetDeclaredMethods(true),
name,
parameterTypes)) != null) {
if (includeStaticMethods || !Modifier.isStatic(res.getModifiers()))
return res;
}
// Search superclass's methods
if (!isInterface()) {
Class<? super T> c = getSuperclass();
if (c != null) {
if ((res = c.getMethod0(name, parameterTypes, true)) != null) {
return res;
}
}
}
// Search superinterfaces' methods
Class<?>[] interfaces = getInterfaces();
for (Class<?> c : interfaces)
if ((res = c.getMethod0(name, parameterTypes, false)) != null)
allInterfaceCandidates.add(res);
// Not found
return null;
}
private static Method searchMethods(Method[] methods,
String name,
Class<?>[] parameterTypes)
{
Method res = null;
String internedName = name.intern();
for (int i = 0; i < methods.length; i++) {
Method m = methods[i];
if (m.getName() == internedName
&& arrayContentsEq(parameterTypes, m.getParameterTypes())
&& (res == null
|| res.getReturnType().isAssignableFrom(m.getReturnType())))
res = m;
}
return (res == null ? res : getReflectionFactory().copyMethod(res));
}
private Method[] privateGetDeclaredMethods(boolean publicOnly) {
checkInitted();
Method[] res;
ReflectionData<T> rd = reflectionData();
if (rd != null) {
res = publicOnly ? rd.declaredPublicMethods : rd.declaredMethods;
if (res != null) return res;
}
// No cached value available; request value from VM
res = Reflection.filterMethods(this, getDeclaredMethods0(publicOnly));
if (rd != null) {
if (publicOnly) {
rd.declaredPublicMethods = res;
} else {
rd.declaredMethods = res;
}
}
return res;
}
при звонке
getMethod0
когда он вызывает метод, который является частным для классаprivateGetMethodRecursive
Рекурсивно получить весь этот классpublic
Модифицированный метод (включая родительский класс).существует
privateGetMethodRecursive
Medium сначала будет сопоставлять все методы текущего класса. Если он не существует, он будет искать свой родительский класс. Если родительский класс не существует, он продолжит рекурсию до родительского класса верхнего уровня.Object
. Возвращает копию этого метода, если он существует.
privateGetDeclaredMethods(boolean publicOnly)
Если параметр имеет значение true, возвращаются все общедоступные методы этого класса, в противном случае возвращаются все измененные методы.
согласно сprivateGetDeclaredMethods
, нетрудно понять, почему getMethods() возвращает все публичные методы родительского класса, аgetDeclaredFields()
Get — это все методы-модификаторы текущего метода.
Сделай смелое предположение,getMethods()
Должен быть рекурсивный вызовprivateGetDeclaredMethods(true)
метод, в то время какgetDeclaredMethods()
следует вызывать только один разprivateGetDeclaredMethods(false)
Методы.
getMethods
Взгляните на исходный код:
@CallerSensitive
public Method[] getMethods() throws SecurityException {
checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
return copyMethods(privateGetPublicMethods());
}
private Method[] privateGetPublicMethods() {
checkInitted();
Method[] res;
ReflectionData<T> rd = reflectionData();
if (rd != null) {
res = rd.publicMethods;
if (res != null) return res;
}
//没有可用的缓存值;递归计算值。
//从获取公共声明的方法开始
MethodArray methods = new MethodArray();
{
//请注意这里
Method[] tmp = privateGetDeclaredMethods(true);
methods.addAll(tmp);
}
...
return res;
}
getDeclaredMethods
Исходный код выглядит следующим образом:
@CallerSensitive
public Method[] getDeclaredMethods() throws SecurityException {
checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
//请注意这里
return copyMethods(privateGetDeclaredMethods(false));
}
Это было именно так, как и ожидалось.
Наконец еще одинgetDeclaredMethod
Метод не представлен, но я полагаю, что каждый может догадаться о процессе реализации после прочтения приведенного выше исходного кода.
@CallerSensitive
public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
throws NoSuchMethodException, SecurityException {
checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
Method method = searchMethods(privateGetDeclaredMethods(false), name, parameterTypes);
if (method == null) {
throw new NoSuchMethodException(getName() + "." + name + argumentTypesToString(parameterTypes));
}
return method;
}
Весь приведенный выше анализ исходного кода о методе приобретения Class был разобран. ~(@^_^@)~ Если вам это нравится, пожалуйста, поставьте лайк. Ваша поддержка - самая большая мотивация для моего обновления.
Ввиду ограниченности техники автора, в статье неизбежны некоторые ошибки, добро пожаловать ━(`∀´)ノПравильно!
использованная литература
Полезность аннотации jvm @CallerSensitive
Все говорят, что отражение Java неэффективно, знаете причину?