servlet解析演进(6)-类加载器(1)

何为类加载器?
网上说:加载类的工具。我以为做为编程人员,把他定义成可以加载类的一段代码更加可以去除概念的晦涩。
何为类加载器加载了类?
一个类被类加载器加载了的意思就是经过类加载器把类的二进制加载到内存中.也就是说在类加载器加载某个类以前。这个类已是.class文件了,编译器作了这件事情。
什么样的类能够被加载?
1):本地编译好的class中直接加载
2):网络加载:java.net.URLClassLoader能够加载url指定的类
3):从jar、zip等等压缩文件加载类,自动解析jar文件找到class文件去加载util类
4):从java源代码文件动态编译成为class文件

类加载过程当中都发生了什么?
编译器将java代码编译成了.class文件这只是万里长城走完了第一步。虚拟机是如何掌握这个.class文件的操纵权的呢,这个时候就须要参谋长类加载器去作这件事情。虚拟机的类加载机制以下:
一、装载(Load)
查找并加载类的二进制数据;
二、连接(Link)
   2.一、校验(Verify):确保被加载类的正确性;
   2.二、准备(Prepare):为类的静态变量分配内存,并将其初始化为默认值;
   2.三、解析(Resolve):把类中的符号引用转换为直接引用;
三、初始化(Initialize):为类的静态变量赋予正确的初始值;java


从这个类加载过程来看,“参谋长”要作的事情却是真多,也看出来“参谋长”的能力有多强。接下来咱们就来仔细看看咱们的“参谋长”。
首先咱们看看“参谋长”的长相和“参谋长”的脾气。
jvm的类加载器的结构如图所示:web


(1) Bootstrap ClassLoader : 将 存放于<JAVA_HOME>\lib目录中的,或者被-Xbootclasspath参数所指定的路径中的,而且是虚拟机识别的(仅按照文 件名识别,如 rt.jar 名字不符合的类库即便放在lib目录中也不会被加载)类库加载到虚拟机内存中。启动类加载器没法被Java程序直接引用
 (2) Extension ClassLoader : 将<JAVA_HOME>\lib\ext目录下的,或者被java.ext.dirs系统变量所指定的路径中的全部类库加载。开发者能够直接使用扩展类加载器。
(3) Application ClassLoader : 负责加载用户类路径(ClassPath)上所指定的类库,开发者可直接使用。
(4)自定义类加载器:
属于应用程序根据自身须要自定义的ClassLoader,如tomcat、jboss都会根据j2ee规范自行实现ClassLoader。

“参谋长”对待须要加载的类遵照双亲委派模型的原则。 双亲委派模型的工做过程:若是一个类加载器收到了类加载的请求,它首先不会本身去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每个层次的 类加载器都是如此,所以全部的加载请求最终都应该传送到顶层的启动类加载器中,只有当父加载器反馈本身没法完成这个加载请求(它的搜索范围中没有找到所需 的类)时,子加载器才会本身去加载。


以上都是JVM类加载器的知识点,分水岭了哈,来讲说servlet服务器tomcat的类加载器。
再说tomcat(7)服务器的类加载器。tomcat服务器毕竟是要符合本身的业务须要因此在原则不变的基础上同jvm加载器是有必定的差别。做为优秀的java web服务器他须要解决本身的问题:
①   同一个Web服务器里,各个Web项目之间各自使用的Java类库要互相隔离。
②   同一个Web服务器里,各个Web项目之间能够提供共享的Java类库。
③   服务器为了避免受Web项目的影响,应该使服务器的类库与应用程序的类库互相独立。
④   对于支持JSP的Web服务器,应该支持热插拔(hotswap)功能。
以上的东西太多太复杂,本文就介绍简单的加载器。(代码针对tomcat4)。
tomcat类加载器如图所示:编程


简单的tomcat类加载器须要知足如下需求:缓存

· 要制定类加载器的某些特定规则 tomcat

· 缓存之前加载的类 ·安全

 事先加载类以预备使用服务器


public interface Loader {
 //返回容器使用的java类加载器
    public ClassLoader getClassLoader();
//返回关联加载器的容器
    public Container getContainer();
//设置和类加载器相关联的容器
    public void setContainer(Container container);
//返回和管理器相关联的DefaultContext
    public DefaultContext getDefaultContext();
//设置和管理器相关的DefaultContext    /**
    public void setDefaultContext(DefaultContext defaultContext);
 //返回配置类加载器的代理模式flag值
    public boolean getDelegate();
//设置类加载器的代理模式flag
     public void setDelegate(boolean delegate);
//返回类加载器的版本号、描述信息
    public String getInfo();
//是否能够自动重载
    public boolean getReloadable();
//设置自动重载标示
    public void setReloadable(boolean reloadable);
    // --------------------------------------------------------- 公共方法
//添加该容器的属性变化监听器
    public void addPropertyChangeListener(PropertyChangeListener listener);
//添加类加载器的加载位置   
    public void addRepository(String repository);
//返回类加载器的库加载路径组合
    public String[] findRepositories();
//类加载器是否被修改标示    
    public boolean modified();
    //删除该容器的属性变化监听器  
    public void removePropertyChangeListener(PropertyChangeListener listener);
}
---------------------
public interface Reloader {
//添加新的类加载路径
    public void addRepository(String repository);
//返回全部的类库加载路径
    public String[] findRepositories();
//类加载器是否被修改了
    public boolean modified();
}
---------------------------------
//该类加载器用于从指向 JAR 文件和目录的 URL 的搜索路径加载类和资源。这里假定任何以 '/' 结束的 URL //都是指向目录的。若是不是以该字符结束,则认为该 URL 指向一个将根据须要打开的 JAR 文件。 
//建立 URLClassLoader 实例的 AccessControlContext 线程将在后续加载类和资源时使用。 
//为加载的类默认授予只能访问 URLClassLoader 建立时指定的 URL 的权限。 
public class URLClassLoader extends SecureClassLoader implements Closeable {
    //加载类和资源的url路径
    private final URLClassPath ucp;    
    //加载类和资源的上下文
    private final AccessControlContext acc;
    //为给定的 URL 构造新 URLClassLoader。首先在指定的父类加载器中搜索 //URL,而后按照为类和资源指定的顺序搜索 URL。这里假定任何以 '/' 结束的 URL //都是指向目录的。若是不是以该字符结束,则认为该 URL 指向一个将根据须要下载和打开的 JAR //文件。 若是有安全管理器,该方法首先调用安全管理器的 checkCreateClassLoader //方法以确保容许建立类加载器。
    public URLClassLoader(URL[] urls, ClassLoader parent) {
        super(parent);
        // this is to make the stack depth consistent with 1.1
        SecurityManager security = System.getSecurityManager();
        if (security != null) {
            security.checkCreateClassLoader();
        }
        ucp = new URLClassPath(urls);
        this.acc = AccessController.getContext();
    }
    URLClassLoader(URL[] urls, ClassLoader parent,
                   AccessControlContext acc) {
        super(parent);
        // this is to make the stack depth consistent with 1.1
        SecurityManager security = System.getSecurityManager();
        if (security != null) {
            security.checkCreateClassLoader();
        }
        ucp = new URLClassPath(urls);
        this.acc = acc;
    }
    /**
     使用默认的委托父 ClassLoader 为指定的 URL 构造一个新 URLClassLoader。首先在父类加载器中搜索 URL,而后按照为类和资源指定的顺序搜索 URL。这里假定任何以 '/' 结束的 URL 都是指向目录的。若是不是以该字符结束,则认为该 URL 指向一个将根据须要下载和打开的 JAR 文件。 
    若是有安全管理器,该方法首先调用安全管理器的 checkCreateClassLoader 方法以确保容许建立类加载器。 
     */
    public URLClassLoader(URL[] urls) {
        super();
        // this is to make the stack depth consistent with 1.1
        SecurityManager security = System.getSecurityManager();
        if (security != null) {
            security.checkCreateClassLoader();
        }
        ucp = new URLClassPath(urls);
        this.acc = AccessController.getContext();
    }
    //回给定 codesource 对象的权限。该方法的实现首先调用 super.getPermissions,而后基于 //codesource 的 URL 添加权限。 
//若是此 URL 的协议为 "jar",那么授予的权限将基于 Jar 文件 URL 所请求的权限。 
//若是协议为 "file",而且路径指定了某个文件,则要授予对该文件的读权限。若是协议为 //"file",而且路径是一个目录,则要授予该目录中的全部文件及其(递归)子目录中包含的全部文件读权限。 
    protected PermissionCollection getPermissions(CodeSource codesource)
    {
        PermissionCollection perms = super.getPermissions(codesource);
        URL url = codesource.getLocation();
        Permission p;
        URLConnection urlConnection;
        try {
            urlConnection = url.openConnection();
            p = urlConnection.getPermission();
        } catch (java.io.IOException ioe) {
            p = null;
            urlConnection = null;
        }
        if (p instanceof FilePermission) {
            // if the permission has a separator char on the end,
            // it means the codebase is a directory, and we need
            // to add an additional permission to read recursively
            String path = p.getName();
            if (path.endsWith(File.separator)) {
                path += "-";
                p = new FilePermission(path, SecurityConstants.FILE_READ_ACTION);
            }
        } else if ((p == null) && (url.getProtocol().equals("file"))) {
            String path = url.getFile().replace('/', File.separatorChar);
            path = ParseUtil.decode(path);
            if (path.endsWith(File.separator))
                path += "-";
            p =  new FilePermission(path, SecurityConstants.FILE_READ_ACTION);
        } else {
            /**
             * Not loading from a 'file:' URL so we want to give the class
             * permission to connect to and accept from the remote host
             * after we've made sure the host is the correct one and is valid.
             */
            URL locUrl = url;
            if (urlConnection instanceof JarURLConnection) {
                locUrl = ((JarURLConnection)urlConnection).getJarFileURL();
            }
            String host = locUrl.getHost();
            if (host != null && (host.length() > 0))
                p = new SocketPermission(host,
                                         SecurityConstants.SOCKET_CONNECT_ACCEPT_ACTION);
        }
        // make sure the person that created this class loader
        // would have this permission
        if (p != null) {
            final SecurityManager sm = System.getSecurityManager();
            if (sm != null) {
                final Permission fp = p;
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() throws SecurityException {
                        sm.checkPermission(fp);
                        return null;
                    }
                }, acc);
            }
            perms.add(p);
        }
        return perms;
    }
   //为指定的 URL 和父类加载器建立新 URLClassLoader 实例。若是安装了安全管理器,该方法返回的 //URLClassLoader 的 loadClass 方法将在加载该类以前调用 SecurityManager.checkPackageAccess //方法。 
    public static URLClassLoader newInstance(final URL[] urls,
                                             final ClassLoader parent) {
        // Save the caller's context
        final AccessControlContext acc = AccessController.getContext();
        // Need a privileged block to create the class loader
        URLClassLoader ucl = AccessController.doPrivileged(
            new PrivilegedAction<URLClassLoader>() {
                public URLClassLoader run() {
                    return new FactoryURLClassLoader(urls, parent, acc);
                }
            });
        return ucl;
    }
//加载器工厂类
final class FactoryURLClassLoader extends URLClassLoader {
    static {
        ClassLoader.registerAsParallelCapable();
    }
    FactoryURLClassLoader(URL[] urls, ClassLoader parent,
                          AccessControlContext acc) {
        super(urls, parent, acc);
    }
    FactoryURLClassLoader(URL[] urls, AccessControlContext acc) {
        super(urls, acc);
    }
    public final Class loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        // First check if we have permission to access the package. This
        // should go away once we've added support for exported packages.
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            int i = name.lastIndexOf('.');
            if (i != -1) {
                sm.checkPackageAccess(name.substring(0, i));
            }
        }
        return super.loadClass(name, resolve);
    }
    其余略
}
相关文章
相关标签/搜索