Maison > Java > JavaBase > le corps du texte

Un résumé super détaillé des points techniques du principe de réflexion JVM~

coldplay.xixi
Libérer: 2020-10-10 17:20:14
avant
2325 Les gens l'ont consulté

La colonne

Java Basics présente aujourd'hui un résumé très détaillé des points techniques du principe de réflexion JVM.

Un résumé super détaillé des points techniques du principe de réflexion JVM~

Définition de la réflexion

1, le mécanisme de réflexion JAVA est en état d'exécution

pour tout Une classe peut connaître tous les attributs et méthodes de cette classe ;

Pour n'importe quel objet, elle peut appeler n'importe lequel de ses méthodes et attributs

Ce type d'informations obtenues dynamiquement et dynamiques La fonction de ; l'appel de la méthode d'un objet est appelé mécanisme de réflexion du langage Java.

Fonctions fournies par réflexion :

  • Déterminer la classe à laquelle appartient tout objet au moment de l'exécution
  • Construire un objet de n'importe quelle classe au moment de l'exécution
  • Déterminer les variables membres et les méthodes de n'importe quelle classe au moment de l'exécution
  • Appeler la méthode de n'importe quel objet au moment de l'exécution

(Si l'attribut est privé, normal Ce qui suit ne permet pas des étrangers pour exploiter la valeur de l'attribut. Ici, vous pouvez utiliser la méthode setAccessible(true) de la classe Field pour ouvrir temporairement l'autorisation d'opération)

Scénarios d'utilisation de Reflection

  • Java. Si vous connaissez les informations spécifiques de la classe et de l'objet lors du codage, vous pouvez directement faire fonctionner la classe et l'objet à ce moment, sans réflexion
  • Si vous ne connaissez pas les informations spécifiques de la classe ou de l'objet lors du codage , vous devez utiliser la réflexion à ce moment

Analyse du code source de réflexion

Exemple d'API :

Class.forName("com.my.reflectTest").newInstance()复制代码
Copier après la connexion

Reflection pour obtenir l'instance de classe Class.forName("xxx. ");

Premier appel Utilisez la méthode statique de java.lang.Class pour obtenir des informations sur la classe !

Remarque : la réflexion forName() pour obtenir des informations sur la classe ne laisse pas l'implémentation à Java, mais à la jvm pour le chargement !

<是> La raison principale est d'obtenir d'abord le chargeur de classe, puis d'appeler la méthode Native pour obtenir des informations. La classe de chargement consiste à ajouter un bonus au ClassLoader lors du chargement !

 @CallerSensitive
    public static Class<?> forName(String className)
                throws ClassNotFoundException {
        // 先通过反射,获取调用进来的类信息,从而获取当前的 classLoader
        Class<?> caller = Reflection.getCallerClass();
        // 调用native方法进行获取class信息
        return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
    }复制代码
Copier après la connexion

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);
    }复制代码
Copier après la connexion

3. Sinon, lancez une exception directement ;

  • Trouvez le constructeur sans paramètre et mettez-le en cache

  • Appelez le constructeur sans paramètre de la méthode Constructor spécifique ; , génère des instances et renvoie ;

  • newInstance() 其实相当于调用类的无参构造函数,主要做了三件事复制代码
    Copier après la connexion
  • 4. getConstructor0() consiste à obtenir le constructeur correspondant en trois étapes :

    1. Obtenez d'abord tous les constructeurs, puis par effectuer une comparaison de types de paramètres ; 2. Après avoir trouvé une correspondance, copiez une copie du constructeur via ReflectionFactory et renvoyez-la ; 3. Sinon, lancez NoSuchMethodException;
  • // 首先肯定是 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&#39;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;
            }
        }复制代码
    Copier après la connexion
5. privateGetDeclaredConstructors(), obtenez toutes les étapes principales du constructeur;

1. Essayez d'abord de l'obtenir du cache ; 2. Si le cache n'existe pas, récupérez-le depuis la jvm et stockez-le dans le cache. Le cache utilise des références logicielles pour enregistrer la mémoire afin de garantir que la mémoire est disponible

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));
    }复制代码
Copier après la connexion

De plus, utilisez relationData ; () pour le stockage en cache ; la structure de données de ReflectionData comme suit !

// 获取当前类所有的构造方法,通过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 classRedefinedCount = this.classRedefinedCount;
        ReflectionData<T> rd;
        if (useCaches &&
            reflectionData != null &&
            (rd = reflectionData.get()) != null &&
            rd.redefinedCount == classRedefinedCount) {
            return rd;
        }
        // else no SoftReference or cleared SoftReference or stale ReflectionData
        // -> create and replace new instance
        return newReflectionData(reflectionData, classRedefinedCount);
    }
    
    // 新创建缓存,保存反射信息
    private ReflectionData<T> newReflectionData(SoftReference<ReflectionData<T>> oldReflectionData,
                                                int classRedefinedCount) {
        if (!useCaches) return null;
 
        // 使用cas保证更新的线程安全性,所以反射是保证线程安全的
        while (true) {
            ReflectionData<T> rd = new ReflectionData<>(classRedefinedCount);
            // try to CAS it...
            if (Atomic.casReflectionData(this, oldReflectionData, new SoftReference<>(rd))) {
                return rd;
            }
            // 先使用CAS更新,如果更新成功,则立即返回,否则测查当前已被其他线程更新的情况,如果和自己想要更新的状态一致,则也算是成功了
            oldReflectionData = this.reflectionData;
            classRedefinedCount = this.classRedefinedCount;
            if (oldReflectionData != null &&
                (rd = oldReflectionData.get()) != null &&
                rd.redefinedCount == classRedefinedCount) {
                return rd;
            }
        }
    }复制代码
Copier après la connexion

6. Grâce à ce qui précède, le Constructeur est obtenu ! Ensuite, il vous suffit d'appeler newInstance() de son constructeur correspondant pour renvoyer l'instance !

// 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 classRedefinedCount when we created this ReflectionData instance
        final int redefinedCount;
 
        ReflectionData(int redefinedCount) {
            this.redefinedCount = redefinedCount;
        }
    }复制代码
Copier après la connexion
Après avoir renvoyé l'instance du constructeur, vous pouvez effectuer une conversion de type en fonction de l'environnement externe, afin d'utiliser l'interface ou la méthode pour appeler la fonction d'instance.

Recommandations d'apprentissage gratuites associées :

bases de Java

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:juejin.im
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal