自己写的java.lang.String可以让jvm加载到吗?

git

github.com/fw103699437…
java.lang.String分支

前言

老问题了,到网上也一搜一堆答案,比如,随便来一篇

自己写的java.lang.String可以让jvm加载到吗?_第1张图片

乍一看好像没什么问题,但是在掌握自定义类加载器之后知道如何打破双亲委派之后再回头来看这段话发现有两个问题:
1:凭什么你认为我现在是ApplicationClassLoader? 毕竟很多框架都会自定义类加载器的
2: 凭什么你认为我一定要走双亲委派?
怀疑当然要有个证据,那么我们就写一段程序:
1:打破双亲委派
2:写一个java.lang.String类
代码见git
《2020最新Java基础精讲视频教程和学习路线!》

一些重要的地方截取出来看看

package java.lang;

public class String {
    private Integer a;

    public Integer getA() {
        return a;
    }

    public void setA(Integer a) {
        this.a = a;
    }
} 
package org.wayne;

import org.wayne.util.ClassLoaderUtil;

import java.lang.reflect.Method;

public class RegisterDriverUtil {
    public static void register(String name){
        Class pluginClass = ClassLoaderUtil.getPluginClass(RegisterDriverUtil.class);
        try {
            Method method = pluginClass.getDeclaredMethod("register", String.class);
            method.setAccessible(true);
            method.invoke(pluginClass.newInstance(),name);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

} 
package org.wayne;

public class RegisterDriverUtil {
    public void register(String name) throws ClassNotFoundException {
        Class.forName(name);
    }
} 
 public static void test8(){
        EnvironmentUtil.setEnv(EnvEnum.A);
        RegisterDriverUtil.register("java.lang.String");
        EnvironmentUtil.clearEnv();
    } 

打包,在wsl下运行,结果如下

自己写的java.lang.String可以让jvm加载到吗?_第2张图片

推测:
1:代码里是用自定义类加载器直接加载的java.lang.String类,并没有走双亲委派
2:由报错来看,是java.lang.ClassLoader阻止了加载

原因

无论何种自定义类加载器,最后都是调用的defineClass方法加载byte[],注释如下

自己写的java.lang.String可以让jvm加载到吗?_第3张图片

 protected final Class defineClass(String name, byte[] b, int off, int len,
                                         ProtectionDomain protectionDomain)
        throws ClassFormatError
    {
        protectionDomain = preDefineClass(name, protectionDomain);
        String source = defineClassSourceLocation(protectionDomain);
        Class c = defineClass1(name, b, off, len, protectionDomain, source);
        postDefineClass(c, protectionDomain);
        return c;
    }
    
        /* Determine protection domain, and check that:
        - not define java.* class,
        - signer of this class matches signers for the rest of the classes in
          package.
    */
    private ProtectionDomain preDefineClass(String name,
                                            ProtectionDomain pd)
    {
        if (!checkName(name))
            throw new NoClassDefFoundError("IllegalName: " + name);

        // Note:  Checking logic in java.lang.invoke.MemberName.checkForTypeAlias
        // relies on the fact that spoofing is impossible if a class has a name
        // of the form "java.*"
        if ((name != null) && name.startsWith("java.")) {
            throw new SecurityException
                ("Prohibited package name: " +
                 name.substring(0, name.lastIndexOf('.')));
        }
        if (pd == null) {
            pd = defaultDomain;
        }

        if (name != null) checkCerts(name, pd.getCodeSource());

        return pd;
    } 

结论

1:无论何种自定义类加载器,最终都会调用ClassLoader.defineClass
2:ClassLoader.defineClass中会检查类名,类名以java.开头的,不予加载

链接:https://juejin.cn/post/6898617802286956551

你可能感兴趣的:(spring,java,后端,springboot)