自定义类加载器加载自定义类:能否加载自定义的java.lang.xxxxxx

最近在研究类加载器,看到很多有关自定义类加载器的方法。有关“能否自定义类加载器加载java.lang.String”有争议,于是我实现了一下:

首先实现自定义类加载器可以通过实现两个方法来做:1 findClass 2 loadClass

首先看findClass


如果实现这个方法,那么样例代码如下:

  1.  protected Class<?> findClass(String name) throws ClassNotFoundException  
  2.     {  
  3.         File file = new File("D:/People.class");  
  4.         try{  
  5.             byte[] bytes = getClassBytes(file);  
  6.             //defineClass方法可以把二进制流字节组成的文件转换为一个java.lang.Class  
  7.             Class<?> c = this.defineClass(name, bytes, 0, bytes.length);  //这里注意看因为返回值是class,所以一定会调用defineclass
  8.             return c;  
  9.         }   
  10.         catch (Exception e)  
  11.         {  
  12.             e.printStackTrace();  
  13.         }  
  14.           
  15.         return super.findClass(name);  
  16.     }  

然后再查看defineclass发现defineclass是classloader的方法,方法如下:

protected final Class<?> defineClass(String name, byte[] b, int off, int len)
        throws ClassFormatError
    {
        return defineClass(name, b, off, len, null);
    }


注意这个是final方法,同样defineclass都是final方法。java编程思想中解释final如下:

“使用final方法的原因有两个。第一个原因是把方法锁定,以防任何继承类修改它的含义;第二个原因是效率。在早期的Java实现版本中,会将final方法转为内嵌调用。但是如果方法过于庞大,可能看不到内嵌调用带来的任何性能提升。在最近的Java版本中,不需要使用final方法进行这些优化了。“

扫描二维码关注公众号,回复: 1731774 查看本文章

所以,这个defineclass是无法让子类实现的,所以生成类的方法一定要用ClassLoader里面的defineClass

然后我们再看defineClass:

    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;
    }

发现有preDefineClass方法,再看此方法:

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;
    }

重点代码已加黑,发现一java开头的都会抛出异常。因此只要在findClass方法里面创建class,就逃不过这个方法,所以创建自定义的java.lang.xcxxxxxx都不行


另外一种实现自定义加载的是重写loadClass方法,重写这个方法可以破坏双亲委派模型:java中的loadClass方法如下:

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;
        }
    }

我们在红色代码不用父类加载器即可破坏双亲原则,但是只要生成一个类,就逃不过defineClass方法,所以,无法通过自定义类加载器来实现java.xxxxxx的类加载

猜你喜欢

转载自blog.csdn.net/gdhgr/article/details/79484296