精华内容
下载资源
问答
  • Java 自动加载JAR文件并运行其中的方法
  • JAVA 自动加载

    2010-10-18 15:27:42
    想项目发布的时候,就自动加载。运行里面的方法! 如: public class test{ public void myLoad(){ System.out.println("load.ok!"); } } 上面。在项目发布后。服务器自己就加载运行了。在控制台...
  • Java类加载机制

    2021-05-23 14:03:21
    Java类加载机制 提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录Java类加载机制一、什么是Java类加载?二、类加载器1.启动类加载器 C/C++实现2.扩展类加载器3.应用程序类加载器1....

    Java类加载机制

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


    一、什么是Java的类加载?

    个人理解:我们在Java中创建新对象时,不能够凭空创建,必须有一个模板,这个模板就是我们的一个java.lang.Class对象,有了这个模板,我们就可以按照这个模板去创建新的对象。

    这样就会产生下面几个问题:
    1.谁来加载? ----->类加载器
    2.什么时候加载? ----->当要用的时候,例如创建对象,同时又没有这个对象的Class对象时加载
    3.加载到那里去?----->类中定义的static静态结构加载到方法区,这个Class对象加载到堆中。
    4.怎么加载?---->加载----->链接---->初始化
    例如我们写了一个Person.class类,现在我们要 Person p1=new Person()创建一个新的Person对象,这个任务会交给我们的类加载器,让他去完成创建一个新对象。
    类加载器拿到这个任务后会先判断之前加载过这个对象没有,如果加载过了就不加载,因为模板一个就够了,如果没有加载,那就去加载这个对象。
    1.加载:创建java.lang.Class对象
    (1)先根据全限定类名,读如Person.class二进制文件
    (2)将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
    (3)在堆中生成模板 Person.class对象,同时我们要能找到方法区的静态结构,也是这个对象提供了静态数据的接口。
    2链接:虽然,我们有了一个模板,但是,这个模板有没有问题,能不能用呢?不知道!!!所以我们要去验证、准备、解析
    (1)验证:我们需要先判断这个二进制文件是不是合法的,不合法就不加载。
    (2)准备:为静态结构在方法区开辟内存,而且赋0值。
    (3)解析:二进制文件中常量池部分是符号引用,把符号引用换成直接引用。
    3初始化:
    (1)上面给静态结构赋值0,现在给他们赋我们写的值。

    这个时候我们就有了一个模板,根据这个模板我们就可以去创建我们的对象,为我们的对象分配内存,初始化对象其他成员变量的值。

    二、类加载器

    1.启动类加载器 C/C++实现

    启动类加载器: BootstrapClassLoader,负责加载存放在 JDK\jre\lib(JDK代表JDK的安装目录,下同)下,或被 -Xbootclasspath参数指定的路径中的,并且能被虚拟机识别的类库(如rt.jar,所有的java.开头的类均被 BootstrapClassLoader加载)。启动类加载器是无法被Java程序直接引用的。

    2.扩展类加载器

    扩展类加载器: ExtensionClassLoader,该加载器由 sun.misc.Launcher$ExtClassLoader实现,它负责加载 JDK\jre\lib\ext目录中,或者由 java.ext.dirs系统变量指定的路径中的所有类库(如javax.开头的类),开发者可以直接使用扩展类加载器。

    3.应用程序类加载器

    应用程序类加载器: ApplicationClassLoader,该类加载器由 sun.misc.Launcher$AppClassLoader来实现,它负责加载用户类路径(ClassPath)所指定的类,开发者可以直接使用该类加载器,如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器。

    =====================================================

    1.双亲委派机制/沙箱安全机制:

    如果,我们自己定义了一个Java.lang.String的类,那我们新建一个String类,是我们的类还是系统自己的类:这个问题就是双亲委派机制去解决的:
    (1)收到类加载请求,把请求交给父加载器执行
    (2)父加载器还有父亲,继续上交
    (3)父加载器能加载,就让父加载器加载,加载不了,才自己加载
    这种方法:避免了类的重复加载
    保证线程安全,避免核心API被篡改

    2.两个类什么时候为同一个类:

    1.包名类名相同
    2.类加载器相同

    总结

    我们新建对象需要一个模板,没有模板时我们就去创建这个模板,加载:先找到他,模模糊糊建一个。链接:看他合不合法、给静态结构赋0值,换引用。初始化:给静态结构换上我们赋予的值

    展开全文
  • ...import java.util.HashMap; import java.util.Map;   import org.apache.commons.configuration.ConfigurationException; import org.apache.commons.configuration.PropertiesConf
    package com.util;


    import java.util.HashMap;
    import java.util.Map;  
      
    import org.apache.commons.configuration.ConfigurationException;  
    import org.apache.commons.configuration.PropertiesConfiguration;  
    import org.apache.commons.configuration.reloading.FileChangedReloadingStrategy;  
    import org.apache.log4j.Logger;  
      
      
    /** 
     *  
     * 配置文件properties自动加载类 
     * @author heli 
     * @version 2012-6-5 
     * @see PropertiesAutoLoad 
     * @since 
     */  
    public class PropertiesAutoLoad  
    {  
        /** 
         * Singleton 
         */  
        private static final PropertiesAutoLoad AUTO_LOAD = new PropertiesAutoLoad();  
      
        /** 
         * Configuration 
         */  
        private static PropertiesConfiguration propConfig;  
      
        /** 
         * 自动保存 
         */  
        private static boolean autoSave = true;  
      
        /** 
         * properties文件路径 
         * @param propertiesFile 
         * @return  
         * @see 
         */  
        public static PropertiesAutoLoad getInstance(String propertiesFile)  
        {  
            //执行初始化  
            init(propertiesFile);  
      
            return AUTO_LOAD;  
        }  
      
        /** 
         * 根据Key获得对应的value 
         * @param key 
         * @return  
         * @see 
         */  
        public Object getValueFromPropFile(String key)  
        {  
            return propConfig.getProperty(key); 
        }  
      
        /** 
         * 获得对应的value数组 
         * @param key 
         * @return  
         * @see 
         */  
        public String[] getArrayFromPropFile(String key)  
        {  
            return propConfig.getStringArray(key);  
        }  
      
        /** 
         * 设置属性 
         * @param key 
         * @param value  
         * @see 
         */  
        public void setProperty(String key, String value)  
        {  
            propConfig.setProperty(key, value);  
        }  
      
        /** 
         * 设置属性 
         * @param map  
         * @see 
         */  
        public void setProperty(Map<String, String> map)  
        {  
            for (String key : map.keySet())  
            {  
                propConfig.setProperty(key, map.get(key));  
            }  
        }  
      
        /** 
         * 构造器私有化 
         */  
        private PropertiesAutoLoad()  
        {  
      
        }  
      
        /** 
         * 初始化 
         * @param propertiesFile  
         * @see 
         */  
        private static void init(String propertiesFile)  
        {  
            try  
            {  
                propConfig = new PropertiesConfiguration(propertiesFile);  
      
                //自动重新加载  
                propConfig.setReloadingStrategy(new FileChangedReloadingStrategy());  
      
                //自动保存  
                propConfig.setAutoSave(autoSave);  
            }  
            catch (ConfigurationException e)  
            {  
               e.printStackTrace();
               
            }  
        }  
      
        /** 
         * Test 
         * @param args  
         * @see 
         */  
        public static void main(String[] args)  
        {  
        PropertiesAutoLoad pa = PropertiesAutoLoad.getInstance("config/log/fileNames.properties");
        String [] fn = pa.getArrayFromPropFile("4");
        StringBuffer sb = new StringBuffer();
        for(String s : fn){
        System.out.println(s);
        sb.append(s+1+",");
        }
        pa.setProperty("1",sb.toString());
        }  
      
    }   
    展开全文
  • java类加载

    2020-04-16 23:38:55
    类加载器是负责加载类的一个对象,ClassLoader是一个抽象类。最常见的加载策略是根据的类的全名,然后找到这个类的class文件,然后从文件读取这个类的数据加载到JVM。每个类都能通过getClassLoader方法获取加载这个...

    类加载器是负责加载类的一个对象,ClassLoader是一个抽象类。最常见的加载策略是根据的类的全名,然后找到这个类的class文件,然后从文件读取这个类的数据加载到JVM。每个类都能通过getClassLoader方法获取加载这个类的类加载器。

    数组类的类对象不是由类加载器创建的,而是根据Java运行时的需要自动创建的。 Class#getClassLoader()返回的数组类的类加载器与其元素类型的类加载器相同;如果元素类型是基本类型,则数组类没有类加载器。

    应用程序可以继承 ClassLoader来自定义自己的累加器 ,以便扩展Java虚拟机动态加载类的方式。

    类使用双亲委派模型来加载类,ClassLoader的每个实例都有一个父类加载器。当一个类加载加载类时,它会把这个类加载请求委派给它的父类加载器来加载。JVM的内置类加载器是"bootstrap class loader"”,它的parent为null,但可以作为ClassLoader实例的父级。

    通常,Java虚拟机以与平台相关的方式从本地文件系统加载类。例如,在UNIX系统上,虚拟机从CLASSPATH环境变量定义的目录中加载类。 但是,某些类可能不是来自文件,它们可能来自其他来源,例如网络,或者它们可以由应用程序构建。方法defineClass(String,byte [],int,int)将字节数组转换为类Class的实例,也可以使用 Class#newInstance创建此新定义的类的实例。

    例如,应用程序可以创建网络类加载器以从服务器下载类文件。示例代码可能如下所示:

     ClassLoader loader = new NetworkClassLoader(host,port);
     Object main = loader.loadClass("Main", true).newInstance();
    

    NetworkClassLoader必须实现 findClass方法来加载类,以及自定义加载动作通过loadClassData()方法从网络加载类。一旦下载了构成类的字节,它应该使用defineClass方法 来创建一个类实例。示例实现是:

      class NetworkClassLoader extends ClassLoader {
     *         String host;
     *         int port;
     *
     *         public Class findClass(String name) {
     *             byte[] b = loadClassData(name);
     *             return defineClass(name, b, 0, b.length);
     *         }
     *
     *         private byte[] loadClassData(String name) {
     *             // load the class data from the connection
     *            
     *         }
     *     }
    

    findClass方法参数name要符合JVM定义的规范,以下这些都是合法的类名称:

     1   "java.lang.String"
     2   "javax.swing.JSpinner$DefaultEditor"
     3  "java.security.KeyStore$Builder$FileBuilder$1"
     4   "java.net.URLClassLoader$3$1"
    

    何时出发类加载动作?

    类加载的触发可以分为隐式加载和显示加载。

    隐式加载

    隐式加载包括以下几种情况:

    遇到new、getstatic、putstatic、invokestatic这4条字节码指令时
    对类进行反射调用时
    当初始化一个类时,如果其父类还没有初始化,优先加载其父类并初始化
    虚拟机启动时,需指定一个包含main函数的主类,优先加载并初始化这个主类

    显示加载

    显示加载包含以下几种情况:

    通过ClassLoader的loadClass方法
    通过Class.forName
    通过ClassLoader的findClass方法
    被加载的类存放在哪里?
    JDK8之前会加载到内存中的方法区。
    从JDK8到现在为止,会加载到元数据区。

    都有哪些ClassLoader?

    整个JVM平台提供三类ClassLoader。

    Bootstrap ClassLoader

    加载JVM自身工作需要的类,它由JVM自己实现。它会加载JAVA_HOME/jre/lib下的文件

    ExtClassLoader

    它是JVM的一部分,由sun.misc.Launcher.ExtClassLoader实现,他会加载JAVA_HOME/jre/lib/ext目录中的文件(或由System.getProperty(“java.ext.dirs”)所指定的文件)。

    AppClassLoader

    应用类加载器,我们工作中接触最多的也是这个类加载器,它由sun.misc.Launcher.AppClassLoader实现。它加载由System.getProperty(“java.class.path”)指定目录下的文件,也就是我们通常说的classpath路径。

    双亲委派模型

    双亲委派模型原理
    从JDK1.2之后,类加载器引入了双亲委派模型,其模型图如下:

    在这里插入图片描述

    其中,两个用户自定义类加载器的父加载器是AppClassLoader,AppClassLoader的父加载器是ExtClassLoader,ExtClassLoader是没有父类加载器的,在代码中,ExtClassLoader的父类加载器为null。BootstrapClassLoader也并没有子类,因为他完全由JVM实现。

    双亲委派模型的原理是:当一个类加载器接收到类加载请求时,首先会请求其父类加载器加载,每一层都是如此,当父类加载器无法找到这个类时(根据类的全限定名称),子类加载器才会尝试自己去加载。

    此模型解决的问题

    • 为什么要使用双亲委派模型呢?它可以解决什么问题呢?
      双亲委派模型是JDK1.2之后引入的。根据双亲委派模型原理,可以试想,没有双亲委派模型时,如果用户自己写了一个全限定名为java.lang.Object的类,并用自己的类加载器去加载,同时BootstrapClassLoader加载了rt.jar包中的JDK本身的java.lang.Object,这样内存中就存在两份Object类了,此时就会出现很多问题,例如根据全限定名无法定位到具体的类。
      有了双亲委派模型后,所有的类加载操作都会优先委派给父类加载器,这样一来,即使用户自定义了一个java.lang.Object,但由于BootstrapClassLoader已经检测到自己加载了这个类,用户自定义的类加载器就不会再重复加载了。
      所以,双亲委派模型能够保证类在内存中的唯一性。

    • 双亲委派模型实现原理
      下面从源码的角度看一下双亲委派模型的实现。
      JVM在加载一个class时会先调用classloader的loadClassInternal方法,该方法源码如下

    // This method is invoked by the virtual machine to load a class.
    private Class<?> loadClassInternal(String name)
        throws ClassNotFoundException
    {
        // For backward compatibility, explicitly lock on 'this' when
        // the current class loader is not parallel capable.
        if (parallelLockMap == null) {
            synchronized (this) {
                 return loadClass(name);
            }
        } else {
            return loadClass(name);
        }
    }
    该方法里面做的事儿就是调用了loadClass方法,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 {
                        // 如果父类加载器为null,说明ExtClassLoader也没有找到目标类,则调用BootstrapClassLoader来查找
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }
                // 如果都没有找到,调用findClass方法,尝试自己加载这个类
                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;
        }
    }
    
    • 源码中已经给出了几个关键步骤的说明。
      代码中调用BootstrapClassLoader的地方实际是调用的native方法。
      由此可见,双亲委派模型实现的核心就是这个loadClass方法。

    实现自己的类加载器

    类加载器的作用
    类加载器有啥作用呢?我们再回到上面的源码。
    从上文我们知道JVM通过loadClass方法来查找类,所以,他的第一个作用也是最重要的:在指定的路径下查找class文件(各个类加载器的扫描路径在上文已经给出)。

    然后,当父类加载器都说没有加载过目标类时,他会尝试自己加载目标类,这就调用了findClass方法,可以看一下findClass方法的定义:

    protected Class<?> findClass(String name) throws ClassNotFoundException {
        throw new ClassNotFoundException(name);
    }
    

    可以发现他要求返回一个Class对象实例,这里我通过一个实现类sun.rmi.rmic.iiop.ClassPathLoader来说明一下findClass都干了什么。

    protected Class findClass(String var1) throws ClassNotFoundException {
        // 从指定路径加载指定名称的class的字节流
        byte[] var2 = this.loadClassData(var1);
        // 通过ClassLoader的defineClass来创建class对象实例
        return this.defineClass(var1, var2, 0, var2.length);
    }
    

    他做的事情在注释中已经给出,可以看到,最终是通过defineClass方法来实例化class对象的。
    另外可以发现,class文件字节的获取和处理我们是可以控制的。所以,第二个作用:我们可以在字节流解析这一步做一些自定义的处理。 例如,加解密。

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

    被final掉了,没办法覆写,所以这里看似不能做什么事儿了。

    小结一下:

    通过loadClass在指定的路径下查找文件。
    通过findClass方法解析class字节流,并实例化class对象。
    什么时候需要自己实现类加载器
    当JDK提供的类加载器实现无法满足我们的需求时,才需要自己实现类加载器。
    根据上述类加载器的作用,可能有以下几个场景需要自己实现类加载器

    当需要在自定义的目录中查找class文件时(或网络获取)
    class被类加载器加载前的加解密(代码加密领域)

    如何实现自己的类加载器

    接下来,实现一个在自定义class类路径中查找并加载class的自定义类加载器。

    package com.lordx.sprintbootdemo.classloader;
    
    import java.io.ByteArrayOutputStream;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.InputStream;
    
    /**
     * 自定义ClassLoader
     * 功能:可自定义class文件的扫描路径
     * @author zhiminxu 
     */
    // 继承ClassLoader,获取基础功能
    public class TestClassLoader extends ClassLoader {
    
        // 自定义的class扫描路径
        private String classPath;
    
        public TestClassLoader(String classPath) {
            this.classPath = classPath;
        }
    
        // 覆写ClassLoader的findClass方法
        protected Class<?> findClass(String name) throws ClassNotFoundException {
            // getDate方法会根据自定义的路径扫描class,并返回class的字节
            byte[] classData = getDate(name);
            if (classData == null) {
                throw new ClassNotFoundException();
            } else {
                // 生成class实例
                return defineClass(name, classData, 0, classData.length);
            }
        }
    
    
        private byte[] getDate(String name) {
            // 拼接目标class文件路径
            String path = classPath + File.separatorChar + name.replace('.', File.separatorChar) + ".class";
            try {
                InputStream is = new FileInputStream(path);
                ByteArrayOutputStream stream = new ByteArrayOutputStream();
                byte[] buffer = new byte[2048];
                int num = 0;
                while ((num = is.read(buffer)) != -1) {
                    stream.write(buffer, 0 ,num);
                }
                return stream.toByteArray();
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }
    }
    

    使用自定义的类加载器

    package com.lordx.sprintbootdemo.classloader;
    
    public class MyClassLoader {
        public static void main(String[] args) throws ClassNotFoundException {
            // 自定义class类路径
            String classPath = "/Users/zhiminxu/developer/classloader";
            // 自定义的类加载器实现:TestClassLoader
            TestClassLoader testClassLoader = new TestClassLoader(classPath);
            // 通过自定义类加载器加载
            Class<?> object = testClassLoader.loadClass("ClassLoaderTest");
            // 这里的打印应该是我们自定义的类加载器:TestClassLoader
            System.out.println(object.getClassLoader());
        }
    }
    

    跟ClassLoader相关的几个异常

    ClassNotFoundException

    这个异常,相信大家经常遇到。
    那么,到底啥原因导致抛出这个异常呢?
    看一下ClassLoader的源码,在JVM调用loadClassInternal的方法中,就会抛出这个异常。
    其声明如下:

    // This method is invoked by the virtual machine to load a class.
    private Class<?> loadClassInternal(String name)
        throws ClassNotFoundException
    {
        // For backward compatibility, explicitly lock on 'this' when
        // the current class loader is not parallel capable.
        if (parallelLockMap == null) {
            synchronized (this) {
                 return loadClass(name);
            }
        } else {
            return loadClass(name);
        }
    }
    

    这里面loadClass方法会抛出这个异常,再来看loadClass方法

    public Class<?> loadClass(String name) throws ClassNotFoundException {
        return loadClass(name, false);
    }
    调用了重写的loadClass方法
    
    protected Class<?> loadClass(String name, boolean resolve)
            throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // First, check if the class has already been loaded
            // 查看findLoadedClass的声明,没有抛出这个异常
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    if (parent != null) {
                        // 这里会抛,但同样是调loadClass方法,无需关注
                        c = parent.loadClass(name, false);
                    } else {
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // catch住没有抛,因为要在下面尝试自己获取class
                    // 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;
        }
    }
    

    再来看findClass方法

    /**
     * Finds the class with the specified <a href="#name">binary name</a>.
     * This method should be overridden by class loader implementations that
     * follow the delegation model for loading classes, and will be invoked by
     * the {@link #loadClass <tt>loadClass</tt>} method after checking the
     * parent class loader for the requested class.  The default implementation
     * throws a <tt>ClassNotFoundException</tt>.
     *
     * @param  name
     *         The <a href="#name">binary name</a> of the class
     *
     * @return  The resulting <tt>Class</tt> object
     *
     * @throws  ClassNotFoundException
     *          If the class could not be found
     *
     * @since  1.2
     */
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        throw new ClassNotFoundException(name);
    }
    

    果然这里抛出的,注释中抛出这个异常的原因是说:当这个class无法被找到时抛出。这也就是为什么在上面自定义的类加载器中,覆写findClass方法时,如果没有找到class要抛出这个异常的原因。
    至此,这个异常抛出的原因就明确了:在双亲委派模型的所有相关类加载器中,目标类在每个类加载器的扫描路径中都不存在时,会抛出这个异常。

    NoClassDefFoundError

    这也是个经常会碰到的异常,而且不熟悉的同学可能经常搞不清楚什么时候抛出ClassNotFoundException,什么时候抛出NoClassDefFoundError。

    我们还是到ClassLoader中搜一下这个异常,可以发现在defineClass方法中可能抛出这个异常,defineClass方法源码如下:

    /**
     * ... 忽略注释和参数以及返回值的说明,直接看异常声明
     * 
     * @throws  ClassFormatError
     *          If the data did not contain a valid class
     *
     * 在这里。由于NoClassDefFoundError是Error下的,所以不用显示throws
     * @throws  NoClassDefFoundError
     *          If <tt>name</tt> is not equal to the <a href="#name">binary
     *          name</a> of the class specified by <tt>b</tt>
     *
     * @throws  IndexOutOfBoundsException
     *          If either <tt>off</tt> or <tt>len</tt> is negative, or if
     *          <tt>off+len</tt> is greater than <tt>b.length</tt>.
     *
     * @throws  SecurityException
     *          If an attempt is made to add this class to a package that
     *          contains classes that were signed by a different set of
     *          certificates than this class, or if <tt>name</tt> begins with
     *          "<tt>java.</tt>".
     */
    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方法抛出的,这个方法源码如下:

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

    这个校验的源码如下

    // true if the name is null or has the potential to be a valid binary name
    private boolean checkName(String name) {
        if ((name == null) || (name.length() == 0))
            return true;
        if ((name.indexOf('/') != -1)
            || (!VM.allowArraySyntax() && (name.charAt(0) == '[')))
            return false;
        return true;
    }
    

    所以,这个异常抛出的原因是:类名校验未通过。
    但这个异常其实不止在ClassLoader中抛出,其他地方,例如框架中,web容器中都有可能抛出,还要具体问题具体分析。

    展开全文
  • Java类加载

    2020-04-18 00:25:54
    类加载器是负责加载类的一个对象,ClassLoader是一个抽象类。最常见的加载策略是根据的类的全名,然后找到这个类的class文件,然后从文件读取这个类的数据加载到JVM。每个类都能通过getClassLoader方法获取加载这个...

    ClassLoader介绍

    类加载器是负责加载类的一个对象,ClassLoader是一个抽象类。最常见的加载策略是根据的类的全名,然后找到这个类的class文件,然后从文件读取这个类的数据加载到JVM。每个类都能通过getClassLoader方法获取加载这个类的类加载器。
    数组类的类对象不是由类加载器创建的,而是根据Java运行时的需要自动创建的。 Class#getClassLoader()返回的数组类的类加载器与其元素类型的类加载器相同;如果元素类型是基本类型,则数组类没有类加载器。
    应用程序可以继承 ClassLoader来自定义自己的累加器 ,以便扩展Java虚拟机动态加载类的方式。
    类使用双亲委派模型来加载类,ClassLoader的每个实例都有一个父类加载器。当一个类加载加载类时,它会把这个类加载请求委派给它的父类加载器来加载。JVM的内置类加载器是"bootstrap class loader"”,它的parent为null,但可以作为ClassLoader实例的父级。
    通常,Java虚拟机以与平台相关的方式从本地文件系统加载类。例如,在UNIX系统上,虚拟机从CLASSPATH环境变量定义的目录中加载类。 但是,某些类可能不是来自文件,它们可能来自其他来源,例如网络,或者它们可以由应用程序构建。方法defineClass(String,byte [],int,int)将字节数组转换为类Class的实例,也可以使用 Class#newInstance创建此新定义的类的实例。

    何时出发类加载动作?

    类加载的触发可以分为隐式加载和显示加载。
    隐式加载
    隐式加载包括以下几种情况:

    遇到new、getstatic、putstatic、invokestatic这4条字节码指令时
    对类进行反射调用时
    当初始化一个类时,如果其父类还没有初始化,优先加载其父类并初始化
    虚拟机启动时,需指定一个包含main函数的主类,优先加载并初始化这个主类
    显示加载
    显示加载包含以下几种情况:

    通过ClassLoader的loadClass方法
    通过Class.forName
    通过ClassLoader的findClass方法
    被加载的类存放在哪里?
    JDK8之前会加载到内存中的方法区。
    从JDK8到现在为止,会加载到元数据区。
    都有哪些ClassLoader?
    整个JVM平台提供三类ClassLoader。
    Bootstrap ClassLoader
    加载JVM自身工作需要的类,它由JVM自己实现。它会加载JAVA_HOME/jre/lib下的文件
    ExtClassLoader
    它是JVM的一部分,由sun.misc.Launcher.ExtClassLoader实现,他会加载JAVA_HOME/jre/lib/ext目录中的文件(或由System.getProperty(“java.ext.dirs”)所指定的文件)。
    AppClassLoader
    应用类加载器,我们工作中接触最多的也是这个类加载器,它由sun.misc.Launcher.AppClassLoader实现。它加载由System.getProperty(“java.class.path”)指定目录下的文件,也就是我们通常说的classpath路径。

    1.类的加载过程
    JVM将类加载过程分为三个步骤:装载(Load),链接(Link)和初始化(Initialize)链接又分为三个步骤,
    在这里插入图片描述

    1)装载:查找并加载类的二进制数据;
    2)链接

    验证:确保被加载类的正确性;
    准备:为类的静态变量分配内存,并将其初始化为默认值;
    解析:把类中的符号引用转换为直接引用;
    

    3)初始化:为类的静态变量赋予正确的初始值
    那为什么我要有验证这一步骤呢?首先如果由编译器生成的class文件,它肯定是符合JVM字节码格式的,但是万一有高手自己写一个class文件,让JVM加载并运行,用于恶意用途,就不妙了,因此这个class文件要先过验证这一关,不符合的话不会让它继续执行的,也是为了安全考虑吧。
    准备阶段和初始化阶段看似有点牟盾,其实是不牟盾的,如果类中有语句:private static int a = 10,它的执行过程是这样的,首先字节码文件被加载到内存后,先进行链接的验证这一步骤,验证通过后准备阶段,给a分配内存,因为变量a是static的,所以此时a等于int类型的默认初始值0,即a=0,然后到解析(后面在说),到初始化这一步骤时,才把a的真正的值10赋给a,此时a=10。
    类的初始化
    类什么时候才被初始化:

    1)创建类的实例,也就是new一个对象
    2)访问某个类或接口的静态变量,或者对该静态变量赋值
    3)调用类的静态方法4)反射(Class.forName(“com.lyj.load”))
    5)初始化一个类的子类(会首先初始化子类的父类)
    6)JVM启动时标明的启动类,即文件名和类名相同的那个类

    只有这6中情况才会导致类的类的初始化

    类的初始化步骤:

    1)如果这个类还没有被加载和链接,那先进行加载和链接

    2)假如这个类存在直接父类,并且这个类还没有被初始化(注意:在一个类加载器中,类只能初始化一次),那就初始化直接的父类(不适用于接口) 3)加入类中存在初始化语句(如static变量和static块),那就依次执行这些初始化语句。

    3.类的加载

    类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个这个类的Java.lang.Class对象,用来封装类在方法区类的对象。
    在这里插入图片描述
    在这里插入图片描述
    类的加载的最终产品是位于堆区中的Class对象

    Class对象封装了类在方法区内的数据结构,并且向Java程序员提供了访问方法区内的数据结构的接口
    加载类的方式有以下几种:
    1)从本地系统直接加载

    2)通过网络下载.class文件

    3)从zip,jar等归档文件中加载.class文件

    4)从专有数据库中提取.class文件

    5)将Java源文件动态编译为.class文件(服务器)

    4.加载器

    JVM的类加载是通过ClassLoader及其子类来完成的,类的层次关系和加载顺序可以由下图来描述:
    在这里插入图片描述
    1)Bootstrap ClassLoader负责加载$JAVA_HOME中jre/lib/rt.jar里所有的class,由C++实现,不是ClassLoader子类
    2)Extension ClassLoader负责加载java平台中扩展功能的一些jar包,包括 $JAVA_HOME中jre/lib/*.jar或-Djava.ext.dirs指定目录下的jar包
    3)App ClassLoader负责记载classpath中指定的jar包及目录中class
    4)Custom ClassLoader

    属于应用程序根据自身需要自定义的ClassLoader,如tomcat、jboss都会根据j2ee规范自行实现ClassLoader
    加载过程中会先检查类是否被已加载,检查顺序是自底向上,从Custom ClassLoader到BootStrap ClassLoader逐层检查,只要某个classloader已加载就视为已加载此类,保证此类只所有ClassLoader加载一次。而加载的顺序是自顶向下,也就是由上层来逐层尝试加载此类。
    详情请参考:
    https://blog.csdn.net/cutesource/article/details/5904501
    https://blog.csdn.net/gjanyanlig/article/details/6818655

    展开全文
  • 实现了实体和配置文件的关系映射、自动加载、自动保存,基于ORM、持久化的思想,使用Java的注解、反射、范型等特性实现。 依赖jtk_util_0.1.jar,地址:http://download.csdn.net/detail/u010475284/7221887
  • java类加载机制

    2016-11-22 21:22:26
    java类加载过程 寻找jre目录,寻找jvm.dll 初始化JVM; 产生一个BootStrap Loader; BootStrap Loader 自动加载Extend Loader 并将父Loader设置为BootStrap Loader BootStrap Loader 自动加载AppClass Loader,并将其...
  • Java类加载过程内存分析Java类加载过程编译类的加载类加载器运行时数据区执行引擎 Java类加载过程 具体过程如下: 编译 首先Java源代码会被编译器编译成class文件以供计算机执行(详细过程可参考编译原理)。java...
  • 如何让java类自动加载

    2012-10-25 18:24:00
    需求是这样的,如何让一个java类,比如放在了classpath的一个jar包中,不通过任何外部的引用和调用,就能够自动的启动,执行本来的static静态代码块。 这个目的主要是想隐蔽的放一个校验函数,使得java虚拟机一...
  • java类加载顺序

    2020-11-17 16:16:03
    java有枚举: // 定义一个星期的枚举 public enum WeekEnum { // 在第一行显式地列出7个枚举实例(枚举值), //系统会自动添加 public static final 修饰 MONDAY, TUESDAY, WEDNESDAY; } 枚举本质是: ...
  • 关于Java类加载

    2015-03-20 12:46:11
    1.了解类加载的意义类加载Java程序运行的第...2.类加载的一般过程java程序运行的场所是内存,当执行java HelloWorld命令时,JVM会自动将HelloWorld.class文件加载到内存中,并形成一个class的对象HelloWorld.class。
  • Java 类加载

    2009-06-18 22:56:00
    转自:http://www.moon-soft.com/doc/22670.htm<br /><br />第一部分. 提示 我需要读这...本文简要的介绍Java类加载器,然后通过一个构造自定义类加载器 的例子来说明,这个类加载器在加载类前会自动编译代码
  • Java类加载分析

    2018-08-02 20:31:48
    一、加载过程 首先,Jvm在执行时,遇到一个新的时,会到...静态代码块是在类加载自动执行的代码,非静态代码块是在创建对象时自动执行的代码,不创建对象不执行该的非静态代码块。 简单的画个内存运行...
  • JAVA 类加载 实例化 调试

    千次阅读 2010-11-06 13:31:00
    虽然JAVA拥有自动回收内存机制,开发人员完全可以不必担心对象实例回收问题,但是这种机制也导致回收滞后。因此,在开发的时候,内存资源占用问题仍然要考虑。我们可以看下下面这段代码:While(rs.next()){ResultSet...
  • 想必大家对项目开发中,调试文件修改时,容器自动重新加载漫长的过程早已厌倦,现在我们是实现怎么自动加载部署java类吧! 首先下载一个javaRebel (jRebel)这个东西(注意:jrebel.jar3.0的暂时试不成功,可以...
  • 官方文档地址: http://manuals.zeroturnaround.com/jrebel/ide/intellij.html#installation 过期激活 https://blog.csdn.net/alleged/article/details/80659457 激活guid生成 https://www.guidgen.com/ ...
  • Java类加载原理

    2005-09-10 14:38:00
    第一部分. 提示我需要读这篇文章吗?...本文简要的介绍Java类加载器,然后通过一个构造自定义类加载器的例子来说明,这个类加载器在加载类前会自动编译代码。你将学到类加载器到底是干什么的,如何创建你自己的类加
  • 理解Java类加载原理

    2011-05-24 01:44:00
    本文简要的介绍Java类加载器,然后通过一个构造自定义类加载器的例子来说明,这个类加载器在加载类前会自动编译代码。你将学到类加载器到底是干什么的,如何创建你自己的类加载器。只要你有一些基本
  • 解决问题:代码安全扫描 "Classes should not be loaded dynamically",要求 "Remove this use of dynamic class loading." 解决方法:使用jdk自带方法ClassLoader.getSystemClassLoader().loadClass ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 2,567
精华内容 1,026
关键字:

java自动加载类

java 订阅