我是靠谱客的博主 爱听歌小土豆,最近开发中收集的这篇文章主要介绍类加载器--Tomcat--ParallelWebappClassLoader,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

首先是jvm自带的三个类加载器的关系图:
在这里插入图片描述
系统类加载器在加载一个类时,会先查找已经加载的类,如果没找到,再委托父加载器(父加载器不是父类,这是2个概念),父加载器没找到就继续委托父加载器,直到所有的父加载器都没有找到,并且都加载失败之后,就自己加载,如果自己加载也失败了,就抛异常。

父类加载过,而且还尝试加载失败,那么就自己来

    c = findClass(name);

这个方法在urlClassLoader中实现。自定义类加载器,一般覆盖这个方法。调用protected final Class<?> defineClass(String name, byte[] b, int off, int len,
ProtectionDomain protectionDomain)方法即可。

protected Class<?> findClass(final String name)
        throws ClassNotFoundException
    {
        final Class<?> result;
        try {
            result = AccessController.doPrivileged(
                new PrivilegedExceptionAction<Class<?>>() {
                    public Class<?> run() throws ClassNotFoundException {
                        String path = name.replace('.', '/').concat(".class");
                        Resource res = ucp.getResource(path, false);
                        if (res != null) {
                            try {
                                return defineClass(name, res);
                            } catch (IOException e) {
                                throw new ClassNotFoundException(name, e);
                            }
                        } else {
                            return null;
                        }
                    }
                }, acc);
        } catch (java.security.PrivilegedActionException pae) {
            throw (ClassNotFoundException) pae.getException();
        }
        if (result == null) {
            throw new ClassNotFoundException(name);
        }
        return result;
    }
 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;
    }

直接继承ClassLoader的类加载器,需要重写findClass方法,而且这样产生的类加载器依然是双亲委托的。

如果想破坏双亲委托机制,则还需要额外重写loadClass方法。

tomcat 拥有不同的自定义类加载器,以实现对各种资源库的控制。一般来说,tomcat主要用类加载器解决以下4个问题:

  1. 同一个web服务器里,各个web项目之间各自使用的Java类库要相互隔离
  2. 同一个web服务器中,各个web项目之间可以提供共享的Java类库
  3. 为了使服务器不受web项目的影响,应该使服务器的类库与应用程序的类库相互独立
  4. 对于jsp的web服务器,应该支持热插拔功能

tomcat的类加载器定义:
在这里插入图片描述
common类加载器负责加载%catalina_home%/lib,%catalina_base%/lib两个目录下的class文件和jar文件。
在这里插入图片描述在这里插入图片描述tomcat 7中默认左边两个类加载器都是common。

tomcat 7中对这些类加载器的初始化,根据catalina.properties中的server.loader和share.loader属性来判断要不要创建新的类加载器。
在这里插入图片描述
tomcat中有多少个web应用,就有多少个webappclassLader。
它使用ClassLoaderFactory 构建类加载器,common类加载器new URLClassLoader(array, parent);实际上就是urlclassloader。
父加载器是应用类加载器,tomcat会在启动时把当前线程类加载器设置为common类加载器。

public static ClassLoader createClassLoader(File[] unpacked, File[] packed, final ClassLoader parent) throws Exception

public static ClassLoader createClassLoader(List<ClassLoaderFactory.Repository> repositories, final ClassLoader parent) throws Exception
public static enum RepositoryType {
        DIR,   //加载目录下所有资源
        GLOB, //整个目录下所有的jar包资源
        JAR, //单个jar包资源
        URL; //从url上获取的jar包资源

        private RepositoryType() {
        }
    }

ParallelWebappClassLoader这类的主要逻辑是在父类中实现的,子类只有一个方法。用于热部署时重新加载所有的类,重新加载其实只需要新建一个类加载器把类再加载一遍就可以了,这里还保留了加载器之前的状态。

public ParallelWebappClassLoader copyWithoutTransformers() {
        ParallelWebappClassLoader result = new ParallelWebappClassLoader(this.getParent());
        super.copyStateWithoutTransformers(result);

        try {
            result.start();
            return result;
        } catch (LifecycleException var3) {
            throw new IllegalStateException(var3);
        }
    }
protected void copyStateWithoutTransformers(WebappClassLoaderBase base) {
        base.resources = this.resources;
        base.delegate = this.delegate; //默认一般是false
        base.state = LifecycleState.NEW;
        base.clearReferencesStopThreads = this.clearReferencesStopThreads;
        base.clearReferencesStopTimerThreads = this.clearReferencesStopTimerThreads;
        base.clearReferencesLogFactoryRelease = this.clearReferencesLogFactoryRelease;
        base.clearReferencesHttpClientKeepAliveThread = this.clearReferencesHttpClientKeepAliveThread;
        base.jarModificationTimes.putAll(this.jarModificationTimes);
        base.permissionList.addAll(this.permissionList);
        base.loaderPC.putAll(this.loaderPC);
    }

delegate 为true时,这里的父类加载器 不是Java语法中的父类,只是单纯的父加载器。parent属性指定的。
在这里插入图片描述

为false时
在这里插入图片描述

再看核心方法start(),很明显,在重新创建一个新的类加载器后,会调用这个方法:

public void start() throws LifecycleException {
        this.state = LifecycleState.STARTING_PREP;
        WebResource classes = this.resources.getResource("/WEB-INF/classes");
        if(classes.isDirectory() && classes.canRead()) {
            this.localRepositories.add(classes.getURL());
        }

        WebResource[] jars = this.resources.listResources("/WEB-INF/lib");
        WebResource[] var3 = jars;
        int var4 = jars.length;

        for(int var5 = 0; var5 < var4; ++var5) {
            WebResource jar = var3[var5];
            if(jar.getName().endsWith(".jar") && jar.isFile() && jar.canRead()) {
                this.localRepositories.add(jar.getURL());
                this.jarModificationTimes.put(jar.getName(), Long.valueOf(jar.getLastModified()));
            }
        }

        this.state = LifecycleState.STARTED;
    }

父类WebappClassLoaderBase的核心代码是下面的部分,读取字节码,调用转换器,加载类。这些步骤和普通加载器区别不大。

 	 byte[] binaryContent = resource.getContent();
          Manifest manifest = resource.getManifest();
        URL codeBase = resource.getCodeBase();
         Certificate[] certificates = resource.getCertificates();
          String packageName;
       if(this.transformers.size() > 0) {
                packageName = path.substring(1, path.length() - ".class".length());
                 Iterator var12 = this.transformers.iterator();

               while(var12.hasNext()) {
                          ClassFileTransformer transformer = (ClassFileTransformer)var12.next();

                           try {
                                    byte[] transformed = transformer.transform(this, packageName, (Class)null, (ProtectionDomain)null, binaryContent);
                                 if(transformed != null) {
                                     binaryContent = transformed;
                                }
                         } catch (IllegalClassFormatException var18) {
                                        log.error(sm.getString("webappClassLoader.transformError", new Object[]{name}), var18);
                                        return null;
                                    }
                                }
                            }

                            packageName = null;
                            int pos = name.lastIndexOf(46);
                            if(pos != -1) {
                                packageName = name.substring(0, pos);
                            }

                            Package pkg = null;
                            if(packageName != null) {
                                pkg = this.getPackage(packageName);
                                if(pkg == null) {
                                    try {
                                        if(manifest == null) {
                                            this.definePackage(packageName, (String)null, (String)null, (String)null, (String)null, (String)null, (String)null, (URL)null);
                                        } else {
                                            this.definePackage(packageName, manifest, codeBase);
                                        }
                                    } catch (IllegalArgumentException var17) {
                                        ;
                                    }

                                    pkg = this.getPackage(packageName);
                                }
                            }

                            if(this.securityManager != null && pkg != null) {
                                boolean sealCheck = true;
                                if(pkg.isSealed()) {
                                    sealCheck = pkg.isSealed(codeBase);
                                } else {
                                    sealCheck = manifest == null || !this.isPackageSealed(packageName, manifest);
                                }

                                if(!sealCheck) {
                                    throw new SecurityException("Sealing violation loading " + name + " : Package " + packageName + " is sealed.");
                                }
                            }

                            try {
                                clazz = this.defineClass(name, binaryContent, 0, binaryContent.length, new CodeSource(codeBase, certificates));
                            } catch (UnsupportedClassVersionError var16) {
                                throw new UnsupportedClassVersionError(var16.getLocalizedMessage() + " " + sm.getString("webappClassLoader.wrongVersion", new Object[]{name}));
                            }

                            entry.loadedClass = clazz;
                            return clazz;
                        }

最后

以上就是爱听歌小土豆为你收集整理的类加载器--Tomcat--ParallelWebappClassLoader的全部内容,希望文章能够帮你解决类加载器--Tomcat--ParallelWebappClassLoader所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(44)

评论列表共有 0 条评论

立即
投稿
返回
顶部