it_worker365

   ::  ::  ::  ::  :: 管理

类加载机制:

程序启动时,根据入口函数调用相关功能,功能在不同类中即在不同的class文件中,jvm根据类加载机制来动态加载class文件到内存中,只有被加载后才能被调用,否则引发异常

1、装载:查找和导入Class文件

2、链接:其中解析步骤是可以选择的

   (a)检查:检查载入的class文件数据的正确性

   (b)准备:给类的静态变量分配存储空间

   (c)解析:将符号引用转成直接引用

3、初始化:对静态变量,静态代码块执行初始化工作

应用加载一个类,首先委托给父家在第,然后从顶层的bootstrap classloader试图加载,如果没加载成功,则往下传递,直到最下层加载器去到指定文件系统或网络URL中去加载,都没有就ClassNotFoundException,否则将这个找到的类生成一个定义,并加载到内存,返回这个类在内存中的class实例对象

避免重复加载(确保只有一个加载),安全(不能自己写一个String加进去)

类相同 = 类名相同 + 加载该类的类加载器相同

BootStrap ClassLoader JDK核心类库加载(rt.jar/resource.jar/charset.jar等)C++编写,嵌入JVM内核,启动JVM时随之启动,负责加载核心类库并构造下面两种类加载器  "sun.boot.class.path"

Extension ClassLoader 加载扩展类(jre/lib/ext下的jar) "java.ext.dirs"

App ClassLoader 系统类加载器(应用下的jar和class)  "java.class.path"

java -Xbootclasspath/a:path被指定的文件追加到默认的bootstrap路径中
查看核心类库加载信息,两种方式:
        URL[] urls = sun.misc.Launcher.getBootstrapClassPath().getURLs();
        for (int i = 0; i < urls.length; i++) {
            System.out.println(urls[i].toExternalForm());
        }

        System.out.println(System.getProperty("sun.boot.class.path"));
查看类加载器流程:
        ClassLoader loader = TestClassLoaderIssue.class.getClassLoader();
        while(loader != null) {
            System.out.println(loader);
            loader = loader.getParent();
        }
        System.out.println(loader);
sun.misc.Launcher$AppClassLoader@21ff3fcf
sun.misc.Launcher$ExtClassLoader@7cb64078
null
典型应用:
Apache Tomcat
每个web应用都有一个对应的类加载实例,该类加载器也使用代理模式,它首先尝试加载该类,如果找不到再代理给父类加载器,与一般顺序相反,目的是为了让web应用自己的类优先级高于web容器提供的类,核心类库不在该查找范围内,保证了核心类库的安全性
OSGI
每个模块都有一个对应的类加载器,它负责加载模块自己包含的java包和类,当它需要核心类库就代理给父类完成,当需要加载所导入的java类时,代理给到处此类的模块来完成加载
bundleA--classloaderA com.bundleA.A export
bundleB--classloaderB import com.bundleA.A com.bundleB.B extends A
B启动时,classloaderB需要加载类com.bundleB.B,进而需要加载类com.bundleA.A,由于A是import,所以classloaderB将它的加载代理给classloaderA,classloaderA在其内部查找A,所得到的泪A实例就可以被所生命导入了此类的模块使用。对于以java开头的类还是由父类加载器来实现
这种加载方式可以使泪的不同版本共存在JVM中,提高灵活性
 

 为什么要创建自己的类加载器管理类加载:

1. 因为 java提供的只能加载指定目录下的jar和class,其他位置或者网络无法方便加载

2. Class文件需要加密,先将classa进行加密,然后按照规则编写自定义的ClassLoader进行解密,这样我们就可以在程序中加载特定了类,并且这个类只能被我们自定义的加载器进行加载,提高了程序的安全性

3. 可以多版本共存,灵活

继承ClassLoader重写findClass,将class二进制流读入

1. 创建一个类, 编译后将class文件放在d:\com\timer\下

package com.timer;
/**
 * Created by itworker365 on 5/16/2017.
 */
public class LoadedByClassFileClass {
    public LoadedByClassFileClass(){
        System.out.println("LoadedByClassFileClass loaded111 by class loader");
    }
}

2. 创建一个类加载器,测试类加载

package com.timer;
import java.io.*;
/**
 * Created by itworker365 on 5/16/2017.
 */
public class TestClassLoaderIssue extends ClassLoader{
    private String path = "d://";
    private final String fileType = ".class";

    public TestClassLoaderIssue() {
        super();
    }
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        byte[] data = this.loadClassData(name);
        return this.defineClass(name, data, 0, data.length);
    }
    private byte[] loadClassData(String name) {
        InputStream in = null;
        byte[] data = null;
        ByteArrayOutputStream baos = null;
        try {
            name = name.replace(".", "\\");
            in = new BufferedInputStream(new FileInputStream(new File(path + name + fileType)));
            baos = new ByteArrayOutputStream();
            int ch = 0;
            while (-1 != (ch = in.read())) {
                baos.write(ch);
            }
            data = baos.toByteArray();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    baos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return data;
    }
    public static void main(String[] args) {
        TestClassLoaderIssue loader = new TestClassLoaderIssue();
        Class<?> clazz = null;
        Object object;
        try {
            clazz = loader.loadClass("com.timer.LoadedByClassFileClass");
            object = clazz.newInstance();
            System.out.println();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

加入jvm参数 -XX:+TraceClassLoading,可以看到类加载的具体情况

[Loaded sun.security.action.GetBooleanAction from C:\Program Files\Java\jdk1.7.0_04\jre\lib\rt.jar]
[Loaded com.timer.LoadedByClassFileClass from __JVM_DefineClass__]
[Loaded java.net.InetAddress$Cache from C:\Program Files\Java\jdk1.7.0_04\jre\lib\rt.jar]

查看jar中包含的类:jar -tvf slf4j-api-1.7.5.jar

posted on 2017-05-17 10:28  it_worker365  阅读(444)  评论(0编辑  收藏  举报