zoukankan      html  css  js  c++  java
  • 理解Tomcat的WebappClassLoader(web应用类加载器)

    我目前的系统可能需要自己实现类加载器,想要参考Tomcat的实现。关于Tomcat的类加载机制,网上文章很多,当然大多都是互相copy,有价值的信息并不多,不得已我开始看Tomcat代码,略有所得,记录起来。主要针对WebappClassLoader。
     
    负责Web应用的类加载的是org.apache.catalina.loader.WebappClassLoader,它几个比较重要的方 法:findClass(),loadClass(),findClassInternal(),findResourceInternal().类加载 器被用来加载一个类的时候,loadClass()会被调用,loadClass()则调用findClass()。后两个方法是 WebappClassLoader的私有方法,findClass()调用findClassInternal()来创建class对象,而 findClassInternal()则需要findResourceInternal()来查找.class文件。
     
    通常自己实现类记载器只要实现findclass即可,这里为了实现特殊目的而override了loadClass().
     
    下面是精简过的代码(去除了几乎全部关于log、异常和安全控制的代码):
     
    findClass:
     1 public Class findClass(String name) throws ClassNotFoundException {
    2 // 先试图自己加载类,找不到则请求parent来加载
    3 // 注意这点和java默认的双亲委托模式不同
    4 Class clazz = null;
    5 clazz = findClassInternal(name);
    6 if ((clazz == null) && hasExternalRepositories) {
    7 synchronized (this) {
    8 clazz = super.findClass(name);
    9 }
    10 }
    11 if (clazz == null) {
    12 throw new ClassNotFoundException(name);
    13 }
    14
    15 return (clazz);
    16 }
    loadClass:
     1 public Class loadClass(String name, boolean resolve)
    2 throws ClassNotFoundException {
    3 Class clazz = null;
    4 // (0) 先从自己的缓存中查找,有则返回,无则继续
    5 clazz = findLoadedClass0(name);
    6 if (clazz != null) {
    7 if (resolve) resolveClass(clazz);
    8 return (clazz);
    9 }
    10 // (0.1) 再从parent的缓存中查找
    11 clazz = findLoadedClass(name);
    12 if (clazz != null) {
    13 if (resolve) resolveClass(clazz);
    14 return (clazz);
    15 }
    16 // (0.2) 缓存中没有,则首先使用system类加载器来加载
    17 clazz = system.loadClass(name);
    18 if (clazz != null) {
    19 if (resolve) resolveClass(clazz);
    20 return (clazz);
    21 }
    22 //判断是否需要先让parent代理
    23 boolean delegateLoad = delegate || filter(name);
    24 // (1) 先让parent加载,通常delegateLoad == false,即这一步不会执行
    25
    26 if (delegateLoad) {
    27 ClassLoader loader = parent;
    28 if (loader == null)
    29 loader = system;
    30 clazz = loader.loadClass(name);
    31 if (clazz != null) {
    32 if (resolve) resolveClass(clazz);
    33 return (clazz);
    34 }
    35 }
    36 // (2) delegateLoad == false 或者 parent加载失败,调用自身的加载机制
    37 clazz = findClass(name);
    38 if (clazz != null) {
    39 if (resolve) resolveClass(clazz);
    40 return (clazz);
    41 }
    42 // (3) 自己加载失败,则请求parent代理加载
    43
    44 if (!delegateLoad) {
    45 ClassLoader loader = parent;
    46 if (loader == null)
    47 loader = system;
    48 clazz = loader.loadClass(name);
    49 if (clazz != null) {
    50 return (clazz);
    51 }
    52 }
    53 throw new ClassNotFoundException(name);
    54 }
    findClassInternal:
     1 protected Class findClassInternal(String name)
    2 throws ClassNotFoundException {
    3 if (!validate(name))
    4 throw new ClassNotFoundException(name);
    5 //根据类名查找资源
    6 String tempPath = name.replace('.', '/');
    7 String classPath = tempPath + ".class";
    8 ResourceEntry entry = null;
    9 entry = findResourceInternal(name, classPath);
    10
    11 if (entry == null)
    12 throw new ClassNotFoundException(name);
    13 //如果以前已经加载成功过这个类,直接返回
    14
    15 Class clazz = entry.loadedClass;
    16 if (clazz != null)
    17 return clazz;
    18 //以下根据找到的资源(.class文件)进行:1、定义package;2、对package安全检查;3、定义class,即创建class对象
    19 synchronized (this) {
    20 if (entry.binaryContent == null && entry.loadedClass == null)
    21 throw new ClassNotFoundException(name);
    22 // Looking up the package
    23 String packageName = null;
    24 int pos = name.lastIndexOf('.');
    25 if (pos != -1)
    26 packageName = name.substring(0, pos);
    27 Package pkg = null;
    28 if (packageName != null) {
    29 pkg = getPackage(packageName);
    30 // Define the package (if null)
    31 if (pkg == null) {
    32 //定义package的操作,此处省略,具体参看源码
    33 pkg = getPackage(packageName);
    34 }
    35 }
    36 if (securityManager != null) {
    37 //安全检查操作,此处省略,具体参看源码
    38 }
    39 //创建class对象并返回
    40 if (entry.loadedClass == null) {
    41 try {
    42 clazz = defineClass(name, entry.binaryContent, 0,
    43 entry.binaryContent.length,
    44 new CodeSource(entry.codeBase, entry.certificates));
    45 } catch (UnsupportedClassVersionError ucve) {
    46 throw new UnsupportedClassVersionError(
    47 ucve.getLocalizedMessage() + " " +
    48 sm.getString("webappClassLoader.wrongVersion",
    49 name));
    50 }
    51 entry.loadedClass = clazz;
    52 entry.binaryContent = null;
    53 entry.source = null;
    54 entry.codeBase = null;
    55 entry.manifest = null;
    56 entry.certificates = null;
    57 } else {
    58 clazz = entry.loadedClass;
    59 }
    60 }
    61 return clazz;
    62 }

    findResouceInternal():

    下几篇介绍WebappLoader,StandardContext,StandardWrapper,ApplicationDispatcher在类加载中的作用。其中ApplicationDispatcher是核心。

      1   //要先加载相关实体资源(.jar) 再加载查找的资源本身
    2 protected ResourceEntry findResourceInternal(String name, String path) {
    3 //先根据类名从缓存中查找对应资源 ,有则直接返回
    4 ResourceEntry entry = (ResourceEntry) resourceEntries.get(name);
    5 if (entry != null)
    6 return entry;
    7 int contentLength = -1;//资源二进制数据长度
    8 InputStream binaryStream = null;//资源二进制输入流
    9
    10 int jarFilesLength = jarFiles.length;//classpath中的jar包个数
    11 int repositoriesLength = repositories.length;//仓库数(classpath每一段称为repository仓库)
    12 int i;
    13 Resource resource = null;//加载的资源实体
    14 boolean fileNeedConvert = false;
    15 //对每个仓库迭代,直接找到相应的entry,如果查找的资源是一个独立的文件,在这个代码块可以查找到相应资源,
    16 //如果是包含在jar包中的类,这段代码并不能找出其对应的资源
    17 for (i = 0; (entry == null) && (i < repositoriesLength); i++) {
    18 try {
    19 String fullPath = repositories[i] + path;//仓库路径 加资源路径得到全路径
    20 Object lookupResult = resources.lookup(fullPath);//从资源库中查找资源
    21
    22 if (lookupResult instanceof Resource) {
    23 resource = (Resource) lookupResult;
    24 }
    25 //到这里没有抛出异常,说明资源已经找到,现在构造entry对象
    26 if (securityManager != null) {
    27 PrivilegedAction dp =
    28 new PrivilegedFindResource(files[i], path);
    29 entry = (ResourceEntry)AccessController.doPrivileged(dp);
    30 } else {
    31 entry = findResourceInternal(files[i], path);//这个方式只是构造entry并给其codebase和source赋值
    32 }
    33 //获取资源长度和最后修改时间
    34 ResourceAttributes attributes =
    35 (ResourceAttributes) resources.getAttributes(fullPath);
    36 contentLength = (int) attributes.getContentLength();
    37 entry.lastModified = attributes.getLastModified();
    38 //资源找到,将二进制输入流赋给binaryStream
    39 if (resource != null) {
    40 try {
    41 binaryStream = resource.streamContent();
    42 } catch (IOException e) {
    43 return null;
    44 }
    45 //将资源的最后修改时间加到列表中去,代码略去,参加源码
    46
    47 }
    48
    49 } catch (NamingException e) {
    50 }
    51 }
    52 if ((entry == null) && (notFoundResources.containsKey(name)))
    53 return null;
    54 //开始从jar包中查找
    55 JarEntry jarEntry = null;
    56 synchronized (jarFiles) {
    57 if (!openJARs()) {
    58 return null;
    59 }
    60 for (i = 0; (entry == null) && (i < jarFilesLength); i++) {
    61 jarEntry = jarFiles[i].getJarEntry(path);//根据路径从jar包中查找资源
    62 //如果jar包中找到资源,则定义entry并将二进制流等数据赋给entry,同时将jar包解压到workdir.
    63 if (jarEntry != null) {
    64 entry = new ResourceEntry();
    65 try {
    66 entry.codeBase = getURL(jarRealFiles[i], false);
    67 String jarFakeUrl = getURI(jarRealFiles[i]).toString();
    68 jarFakeUrl = "jar:" + jarFakeUrl + "!/" + path;
    69 entry.source = new URL(jarFakeUrl);
    70 entry.lastModified = jarRealFiles[i].lastModified();
    71 } catch (MalformedURLException e) {
    72 return null;
    73 }
    74 contentLength = (int) jarEntry.getSize();
    75 try {
    76 entry.manifest = jarFiles[i].getManifest();
    77 binaryStream = jarFiles[i].getInputStream(jarEntry);
    78 } catch (IOException e) {
    79 return null;
    80 }
    81 if (antiJARLocking && !(path.endsWith(".class"))) {
    82 //解压jar包代码,参见源码
    83 }
    84 }
    85 }
    86 if (entry == null) {
    87 synchronized (notFoundResources) {
    88 notFoundResources.put(name, name);
    89 }
    90 return null;
    91 }
    92 //从二进制流将资源内容读出
    93 if (binaryStream != null) {
    94 byte[] binaryContent = new byte[contentLength];
    95 //读二进制流的代码,这里省去,参见源码
    96 entry.binaryContent = binaryContent;
    97 }
    98 }
    99 // 将资源加到缓存中
    100 synchronized (resourceEntries) {
    101 ResourceEntry entry2 = (ResourceEntry) resourceEntries.get(name);
    102 if (entry2 == null) {
    103 resourceEntries.put(name, entry);
    104 } else {
    105 entry = entry2;
    106 }
    107 }
    108 return entry;
    109 }



  • 相关阅读:
    dubbo入门(一)
    java中文件操作《一》
    Unity 游戏框架搭建 2019 (七) 自定义快捷键
    凉鞋:我所理解的框架 【Unity 游戏框架搭建】
    Unity 游戏框架搭建 2019 (六) MenuItem 复用
    Unity 游戏框架搭建 2019 (五) 打开所在文件夹
    Unity 游戏框架搭建 2019 (四) 导出 UnityPackage
    Unity 游戏框架搭建 2019 (三) 生成文件名到剪切板
    Unity 游戏框架搭建 2019 (二) 文本复制到剪切板
    Unity 游戏框架搭建 2019 (一) 简介与第一个示例文件名的生成
  • 原文地址:https://www.cnblogs.com/huzhiwei/p/2413830.html
Copyright © 2011-2022 走看看