Android开发进阶—invoke反射及其原理解析

news2024/9/29 3:19:52

反射的概念

  • 反射:Refelection,反射是Java的特征之一,允许运行中的Java程序获取自身信息,并可以操作类或者对象的内部属性
  • 通过反射,可以在运行时获得程序或者程序中的每一个类型的成员活成成员的信息
  • 程序中的对象一般都是在编译时就确定下来,Java反射机制可以动态地创建对象并且调用相关属性,这些对象的类型在编译时是未知的
  • 也就是说 ,可以通过反射机制直接创建对象,即使这个对象类型在编译时是未知的
  • Java反射提供下列功能:
  • 在运行时判断任意一个对象所属的类
  • 在运行时构造任意一个类的对象
  • 在运行时判断任意一个类所具有的成员变量和方法,可以通过反射调用 private 方法
  • 在运行时调用任意一个对象的方法

反射调用方法原理

  1. 首先Java通过字节码获取类的基本信息,其次通过Class#getMethod(“myMethod”)经过查找获取Method对象
  2. 接着通过Method#invoke调用方法, 此处包括Native版本和Java版本实现
  3. Native版本的invoke0()在HotSpot VM里是由JVM_InvokeMethod()支持
  4. JVM_InvokeMethod()中通过JNIHandles::resolve(method)解析方法的符号引用(个人理解, 详情参考R大博客)
  5. 拿到method_handle后, 即符号引用, 获取方法的返回类型等相关信息, 最后调用invoke()执行方法(个人理解, 详情参考R大博客)
  6. 含native版本以及Java版本, native版本初始化快, 但由于对JVM是黑盒, 因此无法内联等优化, 当超过15次时, 会自动转成Java版本, Java版本初始化慢, 但长时间执行效率更高, 可进行内联等优化
public final  
  class Method extends AccessibleObject implements GenericDeclaration, Member {
    public Object invoke(Object obj, Object... args)  
           throws IllegalAccessException, IllegalArgumentException,  
           InvocationTargetException  
   {  
       ...
       return methodAccessor.invoke(obj, args);  
   }  
​
  }
​
// methodAccessor通过ReflectionFactory实例化返回
public class ReflectionFactory {
  public MethodAccessor newMethodAccessor(Method method) {  
        ...
​
        if (noInflation) {  
            return new MethodAccessorGenerator().  
                generateMethod(method.getDeclaringClass(),  
                               method.getName(),  
                               method.getParameterTypes(),  
                               method.getReturnType(),  
                               method.getExceptionTypes(),  
                               method.getModifiers());  
        } else {  
            NativeMethodAccessorImpl acc =  
                new NativeMethodAccessorImpl(method);  
            DelegatingMethodAccessorImpl res =  
                new DelegatingMethodAccessorImpl(acc);  
            acc.setParent(res);  
            return res;  
        }  
    }  
}
​
// native版本则是直接调用Reflection::invoke_method()
class NativeMethodAccessorImpl extends MethodAccessorImpl {  
​
    ...
    public Object invoke(Object obj, Object[] args)  
        throws IllegalArgumentException, InvocationTargetException  
    {  
        if (++numInvocations > ReflectionFactory.inflationThreshold()) {  
            MethodAccessorImpl acc = (MethodAccessorImpl)  
                new MethodAccessorGenerator().  
                    generateMethod(method.getDeclaringClass(),  
                                   method.getName(),  
                                   method.getParameterTypes(),  
                                   method.getReturnType(),  
                                   method.getExceptionTypes(),  
                                   method.getModifiers());  
            parent.setDelegate(acc);  
        }  
     
        return invoke0(method, obj, args);  
    }  
     
    ...
     
    private static native Object invoke0(Method m, Object obj, Object[] args);  
}  
​
// Java版本, 通过MethodAccessorGenerator生成MethodAccessor的实现类
public class GeneratedMethodAccessor1 extends MethodAccessorImpl {      
    ...
​
    public Object invoke(Object obj, Object[] args)     
        throws IllegalArgumentException, InvocationTargetException {  
        ...
        try {  
            target.foo(arg0);  
        } catch (Throwable t) {  
            throw new InvocationTargetException(t);  
        }  
    }  
}

Field

使用反射获取或者修改一个变量的值时,编译器不会进行自动装/拆箱

public class FieldTrouble extends BaseTestClass {
  public Integer value;
​
  public static void main(String[] args) {
      FieldTrouble fieldTrouble = new FieldTrouble();
      Class<? extends FieldTrouble> cls = fieldTrouble.getClass();
      ...
      Field value = cls.getField("value");
      // 抛java.lang.IllegalArgumentException
      value.setInt(fieldTrouble, 23);
      ...
  }
}

访问限制阻止我们修改 final 类型的变量, final变量通过反射修改需要调用Filed#setAccessible(true) 在使用反射修改某个对象的成员变量前你要明白,这样做会造成一定程度的性能开销,因为在反射时这样的操作需要引发许多额外操作,比如验证访问权限等。另外使用反射也会导致一些运行时的计算优化失效

Method

synthetic method合成方法

public class Foo {
​
  private int get(){
    return 1;
  }
  private class Bar {
    private Bar() {
      System.out.println(get());
    }
  }
}
​
...
// Synthetic (合成)方法是由编译器产生的、源代码中没有的方法。当内部类与外部类之前有互相访问 private 属性、方法时,编译器会在运行时为调用方创建一个 synthetic 方法。
static int access$000(Foo); //多出来的 synthetic 方法,为了在 Bar 中的这段代码 System.out.println(get());

varagrs方法

public void testVarargs(String... args) {
    ...
  }
bride桥接方法, 为了兼容JDK 1.5版本以前的代码

反射慢的原因

  • 运行时通过Class对象查找Method等对象
  • 方法调用时, 编译器无法进行方法内联等优化
  • 需要校验, 根据方法名和签名查找方法的符号引用等

反射优化

  • 缓存, 避免多次加载
  • 调用method#setAccessible(true)避免安全检查缓存
  • 直接调用方法

反射的主要用途

  • 反射最重要的用途就是开发各种通用框架
  • 很多框架都是配置化的,通过 XML 文件配置 Bean
  • 为了保证框架的通用性,需要根据配置文件加载不同的对象或者类,调用不同的方法
  • 要运用反射,运行时动态加载需要加载的对象
  • 示例:
  • 在运用 Struts 2 框架的开发中会在 struts.xml 中配置 Action:
<action name="login"
               class="org.ScZyhSoft.test.action.SimpleLoginAction"
               method="execute">
           <result>/shop/shop-index.jsp</result>
           <result name="error">login.jsp</result>
       </action>
123456
  • 配置文件与 Action 建立映射关系
  • 当 View 层发出请求时,请求会被 StrutsPrepareAndExecuteFilter 拦截
  • StrutsPrepareAndExecuteFilter会动态地创建Action实例
  • 请求 login.action
  • StrutsPrepareAndExecuteFilter 会解析 struts.xml 文件
  • 检索 action 中 name 为 login 的 Action
  • 根据 class 属性创建 SimpleLoginAction 实例
  • 使用 invoke 方法调用 execute 方法
  • 反射是各种容器实现的核心

反射的运用

  • 反射相关的类在 StrutsPrepareAndExecuteFilter 包
  • 反射可以用于:
  • 判断对象所属的类
  • 获得class对象
  • 构造任意一个对象
  • 调用一个对象
  • 九大预定义的Class对象:
  • 基本的Java类型: boolean, byte, char, short, int, long, float, double
  • 关键字void通过class属性的也表示为Class对象
  • 包装类或者void类的静态TYPE字段:
  • Integer.TYPE == int.class
  • Integer.class == int.class
  • 数组类型的Class实例对象:
  • Class<String[]> clz = String[].class;
  • 数组的Class对象如何比较是否相等:
  • 数组的维数
  • 数组的类型
  • Class类中的 isArray() ,用来判断是否表示一个数组类型

invoke方法

  • 在Java中很多方法都会调用 invoke 方法,很多异常的抛出多会定位到 invoke 方法:
java.lang.NullPointerException
  at ......
  at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
  at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
  at java.lang.reflect.Method.invoke(Method.java:497)
123456

invoke执行过程

  • invoke 方法用来在运行时动态地调用某个实例的方法,实现如下:
@CallSensitive
public Object invoke(Object obj, Object ... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
    if (!override) {
        if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
            Class<?> caller = Refelection.getCallerClass();
            checkAccess(caller, clazz, obj, modifiers);
        }
    }
    MethodAccessor ma = methodAccessor;
    if (ma == null) {
        ma = acquireMethodAccessor();
    }
    return ma.invoke(obj, args);
}
1234567891011121314

权限检查

  • AccessibleObject类是Field,Method和Constructor对象的基类:

  • 提供将反射的对象标记为在使用时取消默认Java语言访问控制检查的能力

  • invoke方法会首先检查AccessibleObject的override属性的值:

  • override默认值为false:

  • 表示需要权限调用规则,调用方法时需要检查权限

  • 也可以使用 setAccessible() 设置为 true

  • override如果值为true:

  • 表示忽略权限规则,调用方法时无需检查权限

  • 也就是说,可以调用任意private方法,违反了封装

  • 如果override属性为默认值false,则进行进一步的权限检查:

  1. 首先用 Reflection.quickCheckMemberAccess(clazz, modifiers) 方法检查方法是否为 public 1.1 如果是public方法的话,就跳过本步 1.2 如果不是public方法的话,就用Reflection.getCallerClass()方法获取调用这个方法的Class对象,这是一个 native 方法
@CallerSensitive
    public static native Class<?> getCallerClass();
​
----------------------------------------------------------
​
​
- 在OpenJDK中可以找到getCallerClass方法的JNI入口-Reflection.c
​
JNIEXPORT jclass JNICALL Java_sun_reflect_Reflection_getCallerClass__
(JNIEnv *env, jclass unused)
{
    return JVM_GetCallerClass(env, JVM_CALLER_DEPTH);
}
​
----------------------------------------------------------
​
- JVM_GetCallerClass的源码位于jvm.cpp中
​
VM_ENTRY(jclass, JVM_GetCallerClass(JNIEnv* env, int depth))
  JVMWrapper("JVM_GetCallerClass");
  // Pre-JDK 8 and early builds of JDK 8 don't have a CallerSensitive annotation; or
  // sun.reflect.Reflection.getCallerClass with a depth parameter is provided
  // temporarily for existing code to use until a replacement API is defined.
  if (SystemDictionary::reflect_CallerSensitive_klass() == NULL || depth != JVM_CALLER_DEPTH) {
    Klass* k = thread->security_get_caller_class(depth);
    return (k == NULL) ? NULL : (jclass) JNIHandles::make_local(env, k->java_mirror());
  }
  // Getting the class of the caller frame.
  //
  // The call stack at this point looks something like this:
  //
  // [0] [ @CallerSensitive public sun.reflect.Reflection.getCallerClass ]
  // [1] [ @CallerSensitive API.method                                   ]
  // [.] [ (skipped intermediate frames)                                 ]
  // [n] [ caller                                                        ]
  vframeStream vfst(thread);
  // Cf. LibraryCallKit::inline_native_Reflection_getCallerClass
  for (int n = 0; !vfst.at_end(); vfst.security_next(), n++) {
    Method* m = vfst.method();
    assert(m != NULL, "sanity");
    switch (n) {
    case 0:
      // This must only be called from Reflection.getCallerClass
      if (m->intrinsic_id() != vmIntrinsics::_getCallerClass) {
        THROW_MSG_NULL(vmSymbols::java_lang_InternalError(), "JVM_GetCallerClass must only be called from Reflection.getCallerClass");
      }
      // fall-through
    case 1:
      // Frame 0 and 1 must be caller sensitive.
      if (!m->caller_sensitive()) {
        THROW_MSG_NULL(vmSymbols::java_lang_InternalError(), err_msg("CallerSensitive annotation expected at frame %d", n));
      }
      break;
    default:
      if (!m->is_ignored_by_security_stack_walk()) {
        // We have reached the desired frame; return the holder class.
        return (jclass) JNIHandles::make_local(env, m->method_holder()->java_mirror());
      }
      break;
    }
  }
  return NULL;
JVM_END
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263
  1. 获取 Class 对象 caller 后使用 checkAccess 方法进行一次快速的权限校验 ,checkAccess 方法实现如下:
volatile Object securityCheckCache;
​
    void checkAccess(Class<?> caller, Class<?> clazz, Object obj, int modifiers) throws IllegalAccessException {
        if(caller == clazz){    // 快速校验
            return;             // 权限通过校验
        }
        Object cache = securityCheckCache;  // 读取volatile
        Class<?> targetClass = clazz;
        if (obj != null && Modifier.isProtected(modifiers) && ((targetClass = obj.getClass()) != clazz)) {  // 必须匹配caller,targetClass中的一个
            if (cache instanceof Class[]) {
                Class<?>[] cache2 = (Class<?>[]) cache;
                if (cache2[1] == targetClass && cache[0] == caller) {
                    return;     // 校验通过
                }
            }
        } else if (cache == caller) {
            return;             // 校验通过
        }
        slowCheckMemberAccess(caller, clazz, obj, modifiers, targetClass);
    }
1234567891011121314151617181920

首先先执行一次快速校验,一旦 Class 正确则权限检查通过;如果未通过,则创建一个缓存,中间再进行检查

  • 如果上面所有的权限检查都未通过,将会执行更详细的检查:
void slowCheckMemberAccess(Class<?> caller, Class<?> clazz, Object obj, int modifiers, Class<?> targetClass) throws IllegalAccessException {
    Refelection.ensureMemberAccess(caller, clazz, obj, modifiers);
    // 如果成功,就更新缓存
    Object cache = ((targetClass == clazz) ? caller : new Class<?>[] {caller, targetClass});
    securityCheckCache = cache;
}
123456

用 Reflection.ensureMemberAccess 方法继续检查权限.若检查通过就更新缓存,这样下一次同一个类调用同一个方法时就不用执行权限检查了,这是一种简单的缓存机制

由于 JMM 的 happens-before 规则能够保证缓存初始化能够在写缓存之间发生,因此两个cache不需要声明为volatile

  • 检查权限的工作到此结束.如果没有通过检查就会抛出异常,如果通过检查就会到下一步

调用MethodAccessor的invoke方法

  • Method.invoke() 不是自身实现反射调用逻辑,而是通过 sun.refelect.MethodAccessor 来处理

  • Method对象的基本构成:

  • 每个 Java 方法有且只有一个 Method 对象作为 root, 相当于根对象,对用户不可见

  • 当创建 Method 对象时,代码中获得的 Method 对象相当于其副本或者引用

  • root 对象持有一个 MethodAccessor 对象,所有获取到的 Method 对象都共享这一个 MethodAccessor 对象

  • 必须保证 MethodAccessor 在内存中的可见性

  • root对象及其声明:

private volatile MethodAccessor methodAccessor;
/**
 * For sharing of MethodAccessors. This branching structure is
 * currently only two levels deep (i.e., one root Method and
 * potentially many Method objects pointing to it.)
 * 
 * If this branching structure would ever contain cycles, deadlocks can
 * occur in annotation code.
 */
private Method  root;
12345678910
  • MethodAccessor:
/**
 * This interface provides the declaration for
 * java.lang.reflect.Method.invoke(). Each Method object is
 * configured with a (possibly dynamically-generated) class which
 * implements this interface
 */
 public interface MethodAccessor {
    // Matches specification in {@link java.lang.reflect.Method}
    public Object invoke(Object obj, Object[] args) throws IllegalArgumentException, InvocationTargetException;
 }
12345678910

MethodAccessor是一个接口,定义了 invoke() 方法,通过 Usage 可以看出 MethodAccessor 的具体实现类:

  1. sun.reflect.DelegatingMethodAccessorImpl
  2. sun.reflect.MethodAccessorImpl
  3. sun.reflect.NativeMethodAccessorImpl
  • 第一次调用 Java 方法对应的 Method 对象的 invoke() 方法之前,实现调用逻辑的 MethodAccess 对象还没有创建
  • 第一次调用时,才开始创建 MethodAccessor 并更新为 root, 然后调用 MethodAccessor.invoke() 完成反射调用
/**
 * NOTE that there is no synchronization used here. 
 * It is correct(though not efficient) to generate more than one MethodAccessor for a given Method.
 * However, avoiding synchronization will probably make the implementation more scalable.
 */
​
private MethodAccessor acquireMethodAccessor() {
    // First check to see if one has been created yet, and take it if so
    MethodAccessor tmp = null;
    if (root != null)
        tmp = root.getMethodAccessor();
    if (tmp != null) {
        methodAccessor = tmp;
    } else {
        tmp = reflectionFactory.newMethodAccessor(this);
        setMethodAccessor(tmp);
    } 
    return tmp;
}
12345678910111213141516171819
  • methodAccessor 实例由 reflectionFactory 对象操控生成 ,reflectionFactory 是在 AccessibleObject 中声明的:
/**
 * Reflection factory used by subclasses for creating field,
 * method, and constructor accessors. Note that this is called very early in the bootstrapping process.
 */
static final ReflectionFactory reflectionFactory = AccessController.doPrivileged(
                                                    new sun.reflect.ReflectionFactory.GetReflectionFactoryAction());
123456
  • sun.reflect.ReflectionFactory 方法:
public class ReflectionFactory {
    private static boolean initted = false;
    private static Permission reflectionFactoryAccessPerm = new RuntimePermission("reflectionFactoryAccess");
    private static ReflectionFactory soleInstance = new ReflectionFactory();
    // Provides access to package-private mechanisms in java.lang.reflect
    private static volatile LangReflectAccess langReflectAccess;
​
    /**
     * "Inflation" mechanism. Loading bytecodes to implement Method.invoke() and Constructor.
     * newInstance() currently costs 3-4x more than an invocation via native code for the first invocation (though subsequent invocations have been benchmarked to be over 20x faster)
     * Unfortunately this cost increases startup time for certain applications that use reflection intensively (but only once per class) to bootstrap themselves
     * To avoid this penalty we reuse the existing JVM entry points for the first few invocations of Methods and Constructors and then switch to the bytecode-based implementations
     */
​
    // Package-private to be accessible to NativeMethodAccessorImpl and NativeConstructorAccessorImpl
    private static noInflation = false;
    private static int inflationThreshold = 15;
​
    // 生成MethodAccessor
    public MethodAccessor newMethodAccessor(Method method) {
        checkInitted();
​
        if (noInflation && !ReflectUtil.isVMAnonymousClass(method.getDeclaringClass())) {
            return new MethodAccessorGenerator().generateMethod(method.getDeclaringClass(),
                                         method.getName(),
                                         method.getParameterTypes(),
                                         method.getReturnType(),
                                         method.getExceptionTypes(),
                                         method.getModifiers());
        } else {
            NativeMethodAccessorImpl acc = new NativeMethodAccessorImpl(method);
            DelegatingMethodAccessorImpl res = new DelegatingMethodAccessorImpl(acc);
            acc.setParent(res);
            return res;
        }
    }
​
    /**
     * We have to defer full initialization of this class until after the static initializer is run since java.lang.reflect
     * Method's static initializer (more properly, that for java.lang.reflect.AccessibleObject) causes this class's to be run, before the system properties are set up
     */
     private static void checkInitted() {
        if (initted) return;
        AccessController.doPrivileged(
            new PrivilegedAction<Void>() {
                public Void run() {
                /**
                 * Tests to ensure the system properties table is fully initialized
                 * This is needed because reflection code is called very early in the initialization process (before command-line arguments have been parsed and therefore these user-settable properties installed
                 * We assume that if System.out is non-null then the System class has been fully initialized and that the bulk of the startup code has been run
                 */
                 if (System.out == null) {
                        // java.lang.System not yet fully initialized
                        return null;
                    }
                    String val = System.getProperty("sun.reflect.noInflation");
                    if (val != null && val.equals("true")) {
                        noInflation = true;
                    }
                    val = System.getProperty("sun.reflect.inflationThreshold");
                    if (val != null) {
                        try {
                            inflationThreshold = Integer.parseInt(val);
                        } catch (NumberFormatException e) {
                            throw new RuntimeException("Unable to parse property sun.reflect.inflationThreshold", e);
                        }
                    }
                    initted = true;
                    return null;
                }
            });
    }
}
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273
  • 实际的MethodAccessor实现有两个版本,一个是Java版本,一个是native版本,两者各有特点:

  • 初次启动时 Method.invoke() 和 Constructor.newInstance() 方法采用native方法要比Java方法快3-4倍

  • 启动后 native 方法又要消耗额外的性能而慢于 Java 方法

  • Java实现的版本在初始化时需要较多时间,但长久来说性能较好

  • 这是HotSpot的优化方式带来的性能特性:

  • 跨越native边界会对优化有阻碍作用

  • 为了尽可能地减少性能损耗,HotSpot JDK采用inflation方式:

  • Java方法在被反射调用时,开头若干次使用native版

  • 等反射调用次数超过阈值时则生成一个专用的 MethodAccessor 实现类,生成其中的 invoke() 方法的字节码

  • 以后对该Java方法的反射调用就会使用Java版本

  • ReflectionFactory.newMethodAccessor()生成MethodAccessor对象的逻辑:

  • native 版开始会会生成 NativeMethodAccessorImpl 和 DelegatingMethodAccessorImpl 两个对象

  • DelegatingMethodAccessorImpl:

/* Delegates its invocation to another MethodAccessorImpl and can change its delegate at run time */
class DelegatingMethodAccessorImpl extends MethodAccessorImpl {
    private MethodAccessorImpl delegate;
    DelegatingMethodAccessorImpl(MethodAccessorImpl delegate) {
        setDelegate(delegate);
    }
    public Object invoke(Object obj, Object[] args)
        throws IllegalArgumentException, InvocationTargetException
    {
        return delegate.invoke(obj, args);
    }
    void setDelegate(MethodAccessorImpl delegate) {
        this.delegate = delegate;
    }
}
123456789101112131415

DelegatingMethodAccessorImpl对象是一个中间层,方便在 native 版与 Java 版的 MethodAccessor 之间进行切换

  • native版MethodAccessor的Java方面的声明: sun.reflect.NativeMethodAccessorImpl
/* Used only for the first few invocations of a Method; afterward,switches to bytecode-based implementation */
class NativeMethodAccessorImpl extends MethodAccessorImpl {
    private Method method;
    private DelegatingMethodAccessorImpl parent;
    private int numInvocations;
    NativeMethodAccessorImpl(Method method) {
        this.method = method;
    }
    public Object invoke(Object obj, Object[] args)
        throws IllegalArgumentException, InvocationTargetException
    {
        /* We can't inflate methods belonging to vm-anonymous classes 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(method.getDeclaringClass())) {
            MethodAccessorImpl acc = (MethodAccessorImpl)
                new MethodAccessorGenerator().
                    generateMethod(method.getDeclaringClass(),
                                   method.getName(),
                                   method.getParameterTypes(),
                                   method.getReturnType(),
                                   method.getExceptionTypes(),
                                   method.getModifiers());
            parent.setDelegate(acc);
        }
        return invoke0(method, obj, args);
    }
    void setParent(DelegatingMethodAccessorImpl parent) {
        this.parent = parent;
    }
    private static native Object invoke0(Method m, Object obj, Object[] args);
}
12345678910111213141516171819202122232425262728293031
  • 每次 NativeMethodAccessorImpl.invoke() 方法被调用时,程序调用计数器都会增加 1, 看看是否超过阈值
  • 如果超过则调用 MethodAccessorGenerator.generateMethod() 来生成 Java 版的 MethodAccessor 的实现类
  • 改变 DelegatingMethodAccessorImpl 所引用的 MethodAccessor 为 Java 版
  • 经由 DelegatingMethodAccessorImpl.invoke() 调用到的就是 Java 版的实现

invoke总结

以上就是Android开发中的invoke原理反射与原理;更多Android开发可以前往《Android核心技术手册》查看获取海量学习资料;里面内容包含Android开发中进阶技术30几个技术板块。

  • invoke方法的过程:

  • MagicAccessorImpl:
  • 原本Java的安全机制使得不同类之间不是任意信息都可见,但JDK里面专门设了个 MagicAccessorImpl 标记类开了个后门来允许不同类之间信息可以互相访问,由JVM管理
/** <P> MagicAccessorImpl (named for parity with FieldAccessorImpl and
    others, not because it actually implements an interface) is a
    marker class in the hierarchy. All subclasses of this class are
    "magically" granted access by the VM to otherwise inaccessible
    fields and methods of other classes. It is used to hold the code
    for dynamically-generated FieldAccessorImpl and MethodAccessorImpl
    subclasses. (Use of the word "unsafe" was avoided in this class's
    name to avoid confusion with {@link sun.misc.Unsafe}.) </P>
    <P> The bug fix for 4486457 also necessitated disabling
    verification for this class and all subclasses, as opposed to just
    SerializationConstructorAccessorImpl and subclasses, to avoid
    having to indicate to the VM which of these dynamically-generated
    stub classes were known to be able to pass the verifier. </P>
    <P> Do not change the name of this class without also changing the
    VM's code. </P> */
class MagicAccessorImpl {
}
1234567891011121314151617
  • @CallerSensitive注解
Summary: Improve the security of the JDK’s method-handle implementation by replacing the existing
 hand-maintained list of caller-sensitive methods with a mechanism that accurately identifies
  such methods and allows their callers to be discovered reliably.
/**
 * A method annotated @CallerSensitive is sensitive to its calling class,
 * via {@link sun.reflect.Reflection#getCallerClass Reflection.getCallerClass},
 * or via some equivalent.
 *
 * @author John R. Rose
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({METHOD})
public @interface CallerSensitive {
}
1234567891011121314
  • 用@CallerSensitive注解修饰的方法从一开始就知道具体调用此方法的对象

  • 不用再经过一系列的检查就能确定具体调用此方法的对象

  • 实际上是调用 sun.reflect.Reflection.getCallerClass 方法

  • Reflection类位于调用栈中的0帧位置

  • sun.reflect.Reflection.getCallerClass() 方法返回调用栈中从0帧开始的第x帧中的类实例

  • 该方法提供的机制可用于确定调用者类,从而实现"感知调用者(Caller Sensitive)"的行为

  • 即允许应用程序根据调用类或调用栈中的其它类来改变其自身的行为

反射注意点

  • 反射会额外消耗系统资源,如果不需要动态地创建一个对象,就不要使用反射
  • 反射调用方法时可以忽略权限检查.可能会破坏封装性而导致安全问题

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/186422.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

含有吲哚基团132557-72-3,2,3,3-三甲基-3H-吲哚-5-磺酸

基础产品数据&#xff08;Basic Product Data&#xff09;&#xff1a;CAS号&#xff1a;132557-72-3中文名&#xff1a;2,3,3-三甲基-3H-吲哚-5-磺酸&#xff0c;2,3,3-三甲基-3H-吲哚-6-磺酸钾盐英文名&#xff1a;5-Sulfo-2,3,3-trimethyl indolenine sodium salt&#xff0…

Axure原型图设计工具使用记录

Axure原型图设计工具使用记录 Axure快速入门&#xff08;01&#xff09; - 面板介绍 https://blog.51cto.com/u_15294985/3007677 Axure快速入门&#xff08;02&#xff09; - 入门例子&#xff08;登录案例&#xff09; https://developer.aliyun.com/article/1046689 Axu…

03 python 要点(函数+类)

第8章 函数 8.1 定义函数 函数就是执行特定任务的一段代码, 自定义函数的语法格式, 以英文半角冒号结尾. # def: 8.2 调用函数 在定义好函数后&#xff0c;既可调用函数. 8.2.1 使用位置参数调用函数 在调用函数时传递的实参与定义函数时的形参顺序一致&#xff0c;这…

LoongArch上正常使用`pip install`

原创&#xff1a;你在使用loongarch架构操作系统时&#xff0c;是否遇到pip install 安装失败的情况&#xff1f; 刷到这篇文章&#xff0c;大家可添加评论或者私信我&#xff0c;及时满足大家的需求那么&#xff0c;下面讲一下如何配置loongarch架构的pip 仓库及开发原理如何配…

Java集合(五)LinkedList底层扩容源码分析

LinkedList的全面说明&#xff1a; &#xff08;1&#xff09;LinkedList底层实现了双向链表和双端队列特点 &#xff08;2&#xff09;可以添加任意元素&#xff08;元素可以重复&#xff09;&#xff0c;包括null. (3)线程不安全&#xff0c;没有实现同步 LinkedList的底…

MicroBlaze系列教程(1):AXI_GPIO的使用

文章目录 @[toc]简介常用函数使用示例参考资料工程下载本文是Xilinx MicroBlaze系列教程的第1篇文章。 简介 AXI GPIO是基于AXI-lite总线的一个通用输入输出IP核,可配置为一个或两个通道,每个通道32位,每一位可以通过SDK动态配置成输入或输出方向,支持中断请求,配合中断控…

计算机网络第三章 传输层

本文部分图片&#xff08;PPT截图&#xff09;来自中科大计算机网络top down3.0 目录[TOC]3.1 概述传输层TCP和UDP协议可以在IP协议主机到主机通信的基础上&#xff0c;实现进程到进程之间的通信&#xff08;利用端口号&#xff09;真正实现端到端的通信【通过多路复用于解复用…

b站黑马Vue2后台管理项目笔记——(3)用户列表

说明&#xff1a; 此项目中使用的是本地SQL数据库&#xff0c;Vue2。 其他功能请见本人后续的其他相关文章。 本文内容实现的最终效果如下图&#xff1a; 三.用户列表的开发 目标效果&#xff1a; 点击二级菜单——用户列表&#xff0c;在右侧展示用户列表对应的内容&#xf…

羊了个羊,低配纯前端实现,代码开源

各位朋友们大家好&#xff0c;我是克隆窝。 我属实被“羊了个羊”气到了&#xff0c;就是通不过第二关&#xff0c;迫不得已自己弄了个网页版的“鱼了个鱼” 游戏的玩法非常简单&#xff0c;类似 “消消乐”&#xff0c;从一堆方块中找到相同图案的 3 个方块并消除即可。 文末…

火山引擎边缘计算在云边协同方面的探索与实践

作者&#xff1a;杜怀宇近期&#xff0c;由边缘计算产业联盟&#xff08;ECC&#xff09;主办的2022边缘计算产业峰会&#xff08;ECIS2022&#xff09;以云端直播形式成功举办&#xff0c;峰会以“边云智联 助力行业数字化转型”为主题&#xff0c;汇聚来自全球的商业领袖、国…

JavaScript基础复盘1

JS有三种书写位置&#xff0c;分别为行内&#xff0c;内嵌和外部。 注意&#xff0c;引用外部JS文件&#xff0c;script双标签内部不可以写代码了。 JavaScript输入输出语句 变量 变量的使用 变量再使用时分为两步&#xff1a;1.声明变量 2.赋值 实现用JavaScript接受用户输入…

最近公共祖先

最近公共祖先&#xff08;Lowest Common Ancestor&#xff0c;LCA&#xff09; 指两个点的公共祖先中&#xff0c;离根最远/深度最深的 性质&#xff1a; 1.LCA({u})uLCA\left(\left\{u\right\}\right) uLCA({u})u 2.若uuu是vvv的祖先&#xff0c;当且仅当LCA(u,v)uLCA\left(…

[Lua实战]Skynet-1.如何启动(linux环境启动)[开箱可用]

Skynet-如何启动1.依赖环境:可登录&联网的linux(Centos7)系统(可以是虚拟机)2.yum安装依赖库3.git clone skynet项目4.编译skynet4.1有可能遇到的错误(升级gcc到4.9以上即可解决):5.测试运行skynet6.运行结果最近用到了lua,想了解下云风大神的skynet,在网上看了半天也没入门…

Spire.Pdf for Java v9.1.4 Patcher

Spire.PDF for Java是一种 PDF API&#xff0c;它使 Java 应用程序无需使用第三方SDK 即可读取、写入和保存 PDF 文档。使用这个 Java PDF 组件&#xff0c;开发人员和程序员可以实现丰富的功能&#xff0c;从头开始创建 PDF 文件或完全在 Java 应用程序&#xff08;J2SE 和 J2…

leetcode刷题记录总结-7.二叉树

文章目录零、二叉树理论二叉树的种类满二叉树完全二叉树二叉搜索树平衡二叉搜索树二叉树的存储方式二叉树的遍历方式二叉树的定义总结一、二叉树的遍历[144. 二叉树的前序遍历 ](https://leetcode.cn/problems/binary-tree-preorder-traversal/)题解递归实现迭代实现[94. 二叉树…

如何运营企业网站

企业网站的最终目的是给企业带来效益&#xff0c;树立企业形象。只要有这个目标定位&#xff0c;剩下的工作就是围绕这个定位去做和优化&#xff0c;米贸搜整理如下&#xff1a;1.增强被收录页面的重要性。收录页面的提升不仅仅是数量的提升&#xff0c;质量占据了很高的比重。…

网络安全协议

作者简介&#xff1a;一名云计算网络运维人员、每天分享网络与运维的技术与干货。 座右铭&#xff1a;低头赶路&#xff0c;敬事如仪 个人主页&#xff1a;网络豆的主页​​​​​​ 前言 本章将会进行网络安全协议的讲解 一.网络安全 1.什么是网络安全 网络安全&#xff…

导数的概念——“高等数学”

各位CSDN的uu们你们好呀&#xff0c;今天小雅兰的内容是导数的概念&#xff0c;其实在高中时期&#xff0c;我们就已经接触过导数了&#xff0c;但是那个时候学得并不是特别深入&#xff0c;依稀记得&#xff0c;我们当初的导数大题一般都是压轴题&#xff0c;很多学校每次讲解…

Oracle重写sql经典50题(代码)

上次发表的是写的时候遇到的问题&#xff0c;这次备份一下自己的代码&#xff08;很冗余,也不保证正确率&#xff09; mysql50题链接在此 Oracle重写sql经典50题创建数据库和表oracle数据库一个账户就一个库&#xff0c;不需要创建学生表 student创建学生表 student插入学生数据…

A-Star算法探索和实现(五)

本篇摘要在上一篇中我们对寻路的移动规则进行了制定&#xff0c;而在本篇我们将对最佳路径的查找方式进行优化&#xff0c;而这就会涉及到移动规则的检测改进、权值计算的改进、NextNode集的处理改进、寻路逻辑的改进&#xff0c;我们将从上述四个方面进行详细讲解。方案探讨&a…