Tomcat9中类加载体系是怎么样的

发布时间:2021-12-11 12:10:43 作者:小新
来源:亿速云 阅读:207

小编给大家分享一下Tomcat9中类加载体系是怎么样的,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!

1.概要

Tomcat作为Web容器,支持多个应用程序的部署运行,所以会涉及应用程序间类库的隔离和共享,例如:不同应用程序既可以使用某个工具类库的不同版本但互不影响,亦可以共享使用某个类库。

2.JVM双亲委派模型

在JVM中有3种系统提供的类加载器:

我们的应用程序都是由这3种类加载器互相配合进行加载的,还可以加入自己定义的类加载器,他们之间的关系如图所示:
Tomcat9中类加载体系是怎么样的
图中展示的类加载器之间的层次关系,成为类加载器的双亲委派模型(Parents Delegation Model),双亲委派模型要求除了顶层的启动类加载器之外,其余的类加载器都应该有父类加载器。这里类加载器之间的父子关系一般不会继承(Inheritance)的强耦合关系来实现,而是使用组合(Composition)的弱耦合关系来复用父加载器的代码。

类加载器的双亲委派模型在JDK1.2期间被引入并被广泛应用于之后的所有Java程序中,但它并不是一个强制的约束模型,而是Java设计者推荐给开发者的一种类加载器实现方式,在之后章节中会看到,在Apache Tomcat中就打破了此模型。

双亲委派模型的工作过程是:如果一个类加载器收到加载类的请求,他首先不会自己去加载这个类,而是把这个请求委派给父加载器去完成,每个层次的类加载器都是如此处理,所有的加载请求最终都会传送到顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求时(没有找类),子加载器才会尝试自己加载。

双亲委派模型本质上就是委托代理,虽然简单,却使Java类随着类加载器一起具备了优先级层次,例如类java.lang.Object,它存放在rt.jar之中,无论哪一个类加载器加载这个类,最终都是委派给启动类加载器进行加载,最终Object类在程序中的各种类加载器环境中都是同一个类。也就是说,如果你尝试编写一个与rJDK基础类库中已有类重名的Java类,类似java.lang.Object,你会发现,虽然可以正常编译,但永远无法被加载运行。

3.Tomcat9类加载体系

功能健全的Web容器的类加载器需要支持以下几点:

基于以上需求,下图为Tomcat类加载器的委派关系:Tomcat9中类加载体系是怎么样的

4.源码分析

org.apache.catalina.startup.Bootstrap.init()
   /**
     * Initialize daemon.
     * @throws Exception Fatal initialization error
     */
    public void init() throws Exception {
        initClassLoaders(); //初始化类加载器
        Thread.currentThread().setContextClassLoader(catalinaLoader); //设置当前线程类加载器为catalinaLoader
        SecurityClassLoad.securityClassLoad(catalinaLoader); //使用Java SecurityManager初始化
        // Load our startup class and call its process() method
        if (log.isDebugEnabled())
            log.debug("Loading startup class");
        Class<?> startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");
        Object startupInstance = startupClass.getConstructor().newInstance();
        // Set the shared extensions class loader
        if (log.isDebugEnabled())
            log.debug("Setting startup class properties");
        String methodName = "setParentClassLoader";
        Class<?> paramTypes[] = new Class[1];
        paramTypes[0] = Class.forName("java.lang.ClassLoader");
        Object paramValues[] = new Object[1];
        paramValues[0] = sharedLoader; //WebappLoader的parentLoader
        Method method =
            startupInstance.getClass().getMethod(methodName, paramTypes);
        method.invoke(startupInstance, paramValues);
        catalinaDaemon = startupInstance;
    }

commLoader、catalinaLoader和sharedLoader的初始化是在Tomcat容器运行的最初进行的,调用的方法为org.apache.catalina.startup.Bootstrap.init()

org.apache.catalina.startup.Bootstrap.initClassLoaders()
 private void initClassLoaders() {
        try {
            commonLoader = createClassLoader("common", null);
            if (commonLoader == null) {
                // no config file, default to this loader - we might be in a 'single' env.
                commonLoader = this.getClass().getClassLoader();
            }
            catalinaLoader = createClassLoader("server", commonLoader);
            sharedLoader = createClassLoader("shared", commonLoader);
        } catch (Throwable t) {
            handleThrowable(t);
            log.error("Class loader creation threw exception", t);
            System.exit(1);
        }
    }

initClassLoaders方法中,对commLoader、catalinaLoader和sharedLoader进行初始化

org.apache.catalina.startup.Bootstrap.createClassLoader()
private ClassLoader createClassLoader(String name, ClassLoader parent)
        throws Exception {
        String value = CatalinaProperties.getProperty(name + ".loader");
        if ((value == null) || (value.equals("")))
            return parent;
        value = replace(value);
        List<Repository> repositories = new ArrayList<>();
        String[] repositoryPaths = getPaths(value);
        for (String repository : repositoryPaths) {
            // Check for a JAR URL repository
            try {
                @SuppressWarnings("unused")
                URL url = new URL(repository);
                repositories.add(new Repository(repository, RepositoryType.URL));
                continue;
            } catch (MalformedURLException e) {
                // Ignore
            }
            // Local repository
            if (repository.endsWith("*.jar")) {
                repository = repository.substring
                    (0, repository.length() - "*.jar".length());
                repositories.add(new Repository(repository, RepositoryType.GLOB));
            } else if (repository.endsWith(".jar")) {
                repositories.add(new Repository(repository, RepositoryType.JAR));
            } else {
                repositories.add(new Repository(repository, RepositoryType.DIR));
            }
        }
        return ClassLoaderFactory.createClassLoader(repositories, parent);
    }
/**
     * Start this component and implement the requirements
     * of {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
     *
     * @exception LifecycleException if this component detects a fatal error
     *  that prevents this component from being used
     */
    @Override
    protected synchronized void startInternal() throws LifecycleException {
        ...省略其他代码...
        if (getLoader() == null) {
            WebappLoader webappLoader = new WebappLoader(getParentClassLoader());
            webappLoader.setDelegate(getDelegate());
            setLoader(webappLoader);
        }
        ...省略其他代码...
    }

webappLoader的初始化在StandardContext中进行,这里创建WebappLoader

org.apache.catalina.loader.WebappLoader
    //重新实现ClassLoader
    private String loaderClass = ParallelWebappClassLoader.class.getName();
    @Override
    protected void startInternal() throws LifecycleException {
        ...省略其他代码...
        // Construct a class loader based on our current repositories list
        try {
            classLoader = createClassLoader(); //创建webappLoader
            classLoader.setResources(context.getResources());
            classLoader.setDelegate(this.delegate); //是否双亲委派
         ...省略其他代码...
    }
    /**
     * Create associated classLoader.
     */
    private WebappClassLoaderBase createClassLoader()
        throws Exception {
        Class<?> clazz = Class.forName(loaderClass);
        WebappClassLoaderBase classLoader = null;
        if (parentClassLoader == null) {
            parentClassLoader = context.getParentClassLoader();
        }
        Class<?>[] argTypes = { ClassLoader.class };
        Object[] args = { parentClassLoader };
        Constructor<?> constr = clazz.getConstructor(argTypes);
        classLoader = (WebappClassLoaderBase) constr.newInstance(args); //反射创建类加载器
        return classLoader;
    }
org.apache.catalina.loader.ParallelWebappClassLoader
public class ParallelWebappClassLoader extends WebappClassLoaderBase{
    ...省略其他代码...
}
public abstract class WebappClassLoaderBase extends URLClassLoader
        implements Lifecycle, InstrumentableClassLoader, WebappProperties, PermissionCheck {
    ...省略其他代码...
    @Override
    public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
        synchronized (JreCompat.isGraalAvailable() ? this : getClassLoadingLock(name)) {
            if (log.isDebugEnabled())
                log.debug("loadClass(" + name + ", " + resolve + ")");
            Class<?> clazz = null;
            // Log access to stopped class loader
            checkStateForClassLoading(name);
            // (0)检查之前加载过的本地缓存,如果缓存存在,则取缓存中的类
            clazz = findLoadedClass0(name);
            if (clazz != null) {
                if (log.isDebugEnabled())
                    log.debug("  Returning class from cache");
                if (resolve)
                    resolveClass(clazz);
                return clazz;
            }
            // (0.1)检查之前加载过的本地缓存,如果缓存存在,则取缓存中的类
            // GraalVM直接返回null,查询缓存时,会校验name的合法性
            clazz = JreCompat.isGraalAvailable() ? null : findLoadedClass(name);
            if (clazz != null) {
                if (log.isDebugEnabled())
                    log.debug("  Returning class from cache");
                if (resolve)
                    resolveClass(clazz);
                return clazz;
            }
            //(0.2) 通过系统加载器加载类,放置webapp中覆写Java SE的基础类
            //此处的类加载器应为Bootstrap ClassLoader
            String resourceName = binaryNameToPath(name, false);
            ClassLoader javaseLoader = getJavaseClassLoader();
            boolean tryLoadingFromJavaseLoader;
            try {
                URL url;
                if (securityManager != null) {
                    PrivilegedAction<URL> dp = new PrivilegedJavaseGetResource(resourceName);
                    url = AccessController.doPrivileged(dp);
                } else {
                    url = javaseLoader.getResource(resourceName);
                }
                tryLoadingFromJavaseLoader = (url != null);
            } catch (Throwable t) {
                // Swallow all exceptions apart from those that must be re-thrown
                ExceptionUtils.handleThrowable(t);
                // The getResource() trick won't work for this class. We have to
                // try loading it directly and accept that we might get a
                // ClassNotFoundException.
                tryLoadingFromJavaseLoader = true;
            }
            if (tryLoadingFromJavaseLoader) {
                try {
                    clazz = javaseLoader.loadClass(name);
                    if (clazz != null) {
                        if (resolve)
                            resolveClass(clazz);
                        return clazz;
                    }
                } catch (ClassNotFoundException e) {
                    // Ignore
                }
            }
            // (0.5) SecurityManager安全检查
            if (securityManager != null) {
                int i = name.lastIndexOf('.');
                if (i >= 0) {
                    try {
                        securityManager.checkPackageAccess(name.substring(0,i));
                    } catch (SecurityException se) {
                        String error = sm.getString("webappClassLoader.restrictedPackage", name);
                        log.info(error, se);
                        throw new ClassNotFoundException(error, se);
                    }
                }
            }
            boolean delegateLoad = delegate || filter(name, true);
            // (1) 当设置双亲委派或者加载类的名称为Tomcat容器内部的类,则委托给父类加载器加载
            if (delegateLoad) {
                if (log.isDebugEnabled())
                    log.debug("  Delegating to parent classloader1 " + parent);
                try {
                    clazz = Class.forName(name, false, parent);
                    if (clazz != null) {
                        if (log.isDebugEnabled())
                            log.debug("  Loading class from parent");
                        if (resolve)
                            resolveClass(clazz);
                        return clazz;
                    }
                } catch (ClassNotFoundException e) {
                    // Ignore
                }
            }
            // (2) 在应用类加载路径进行加载
            if (log.isDebugEnabled())
                log.debug("  Searching local repositories");
            try {
                clazz = findClass(name);
                if (clazz != null) {
                    if (log.isDebugEnabled())
                        log.debug("  Loading class from local repository");
                    if (resolve)
                        resolveClass(clazz);
                    return clazz;
                }
            } catch (ClassNotFoundException e) {
                // Ignore
            }
            // (3) 委托父类加载器加载
            if (!delegateLoad) {
                if (log.isDebugEnabled())
                    log.debug("  Delegating to parent classloader at end: " + parent);
                try {
                    clazz = Class.forName(name, false, parent);
                    if (clazz != null) {
                        if (log.isDebugEnabled())
                            log.debug("  Loading class from parent");
                        if (resolve)
                            resolveClass(clazz);
                        return clazz;
                    }
                } catch (ClassNotFoundException e) {
                    // Ignore
                }
            }
        }
        throw new ClassNotFoundException(name);
    }
    ...省略其他代码...
}

其中必须通过父类加载加载的类名称通过filter()方法判断:

 protected boolean filter(String name, boolean isClassName) {
        if (name == null)
            return false;
        char ch;
        if (name.startsWith("javax")) {
            /* 5 == length("javax") */
            if (name.length() == 5) {
                return false;
            }
            ch = name.charAt(5);
            if (isClassName && ch == '.') {
                /* 6 == length("javax.") */
                if (name.startsWith("servlet.jsp.jstl.", 6)) {
                    return false;
                }
                if (name.startsWith("el.", 6) ||
                    name.startsWith("servlet.", 6) ||
                    name.startsWith("websocket.", 6) ||
                    name.startsWith("security.auth.message.", 6)) {
                    return true;
                }
            } else if (!isClassName && ch == '/') {
                /* 6 == length("javax/") */
                if (name.startsWith("servlet/jsp/jstl/", 6)) {
                    return false;
                }
                if (name.startsWith("el/", 6) ||
                    name.startsWith("servlet/", 6) ||
                    name.startsWith("websocket/", 6) ||
                    name.startsWith("security/auth/message/", 6)) {
                    return true;
                }
            }
        } else if (name.startsWith("org")) {
            /* 3 == length("org") */
            if (name.length() == 3) {
                return false;
            }
            ch = name.charAt(3);
            if (isClassName && ch == '.') {
                /* 4 == length("org.") */
                if (name.startsWith("apache.", 4)) {
                    /* 11 == length("org.apache.") */
                    if (name.startsWith("tomcat.jdbc.", 11)) {
                        return false;
                    }
                    if (name.startsWith("el.", 11) ||
                        name.startsWith("catalina.", 11) ||
                        name.startsWith("jasper.", 11) ||
                        name.startsWith("juli.", 11) ||
                        name.startsWith("tomcat.", 11) ||
                        name.startsWith("naming.", 11) ||
                        name.startsWith("coyote.", 11)) {
                        return true;
                    }
                }
            } else if (!isClassName && ch == '/') {
                /* 4 == length("org/") */
                if (name.startsWith("apache/", 4)) {
                    /* 11 == length("org/apache/") */
                    if (name.startsWith("tomcat/jdbc/", 11)) {
                        return false;
                    }
                    if (name.startsWith("el/", 11) ||
                        name.startsWith("catalina/", 11) ||
                        name.startsWith("jasper/", 11) ||
                        name.startsWith("juli/", 11) ||
                        name.startsWith("tomcat/", 11) ||
                        name.startsWith("naming/", 11) ||
                        name.startsWith("coyote/", 11)) {
                        return true;
                    }
                }
            }
        }
        return false;
    }
org.apache.catalina.webresources.StandardRoot
public class StandardRoot extends LifecycleMBeanBase implements WebResourceRoot {
    ...省略其他代码...
    private final List<WebResourceSet> preResources = new ArrayList<>();
    private WebResourceSet main;
    private final List<WebResourceSet> classResources = new ArrayList<>();
    private final List<WebResourceSet> jarResources = new ArrayList<>(); //对应WEB-INF/lib
    private final List<WebResourceSet> postResources = new ArrayList<>();
    private final List<WebResourceSet> mainResources = new ArrayList<>(); //对应应用webapp路径
    private final List<List<WebResourceSet>> allResources = new ArrayList<>();
    {
        allResources.add(preResources);
        allResources.add(mainResources);
        allResources.add(classResources);
        allResources.add(jarResources);
        allResources.add(postResources);
    }
    ...省略其他代码...
}

StandardRoot 在容器创建时进行初始化,其实现了接口WebResourceRoot ,用于加载Webapp类库。
以上代码可以看出,在Tomcat中Webapp类加载顺序为:

preResources->mainResources->classResources->jarResources->postResources

其中主要用到的资源路径为:

所以在开发时,可以在src目录下覆盖lib包中的类,因为WEB-INF/classes会有限WEB-INF/lib进行加载。通常我们可以将依赖的第三方类库的源代码复制到src目录经,通过Tomcat运行进行debug,这对于排查第三方类库的问题很有帮助。

org.apache.jasper.JspCompilationContext
public ClassLoader getJspLoader() {
        if( jspLoader == null ) {
            jspLoader = new JasperLoader
                    (new URL[] {baseUrl},
                            getClassLoader(),
                            rctxt.getPermissionCollection());
        }
        return jspLoader;
    }
public void clearJspLoader() {
        jspLoader = null;
    }

以上是“Tomcat9中类加载体系是怎么样的”这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注亿速云行业资讯频道!

推荐阅读:
  1. PG体系结构是怎样的
  2. Kafka的体系架构是怎样的

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

tomcat9

上一篇:如何永久自定义hive函数

下一篇:css如何设置td元素的文本靠上显示

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》