Tomcat类加载,自己看源码

在java本身的类加载器的基础上增加了5个类加载器
在这里插入图片描述

下载tomcat源码:https://tomcat.apache.org/download-90.cgi
找到java/org/apache/catalina/startup/Bootstrap.java
这个是启动类,找到这块,我们来看他的逻辑
main() -> start() -> init() -> initClassLoaders(),依次创建我们的三个类加载器
在这里插入图片描述
看创建类加载器的方法

private ClassLoader createClassLoader(String name, ClassLoader parent)
        throws Exception {
		//得到属性,以common为例,得到common.loader属性,可以在tomcat/conf/catalina.properties中找到
		//common.loader="${catalina.base}/lib","${catalina.base}/lib/*.jar","${catalina.home}/lib","${catalina.home}/lib/*.jar"
        String value = CatalinaProperties.getProperty(name + ".loader");
        if ((value == null) || (value.equals(""))) {
            return parent;
        }
		//下面就是加载tomcat自身字节码文件的逻辑
        value = replace(value);
		//把需要加载的都添加到repositories中
        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));
            }
        }
		//传入repositories
        return ClassLoaderFactory.createClassLoader(repositories, parent);
    }

我们接着跳转到ClassLoaderFactory.createClassLoader()方法

   public static ClassLoader createClassLoader(List<Repository> repositories,
                                                final ClassLoader parent)
        throws Exception {

        if (log.isDebugEnabled()) {
            log.debug("Creating new class loader");
        }

        // Construct the "class path" for this class loader
        //set存储不重复的资源
        Set<URL> set = new LinkedHashSet<>();

        if (repositories != null) {
            for (Repository repository : repositories)  {
                if (repository.getType() == RepositoryType.URL) {
                    URL url = buildClassLoaderUrl(repository.getLocation());
                    if (log.isDebugEnabled()) {
                        log.debug("  Including URL " + url);
                    }
                    set.add(url);
                } else if (repository.getType() == RepositoryType.DIR) {
                    File directory = new File(repository.getLocation());
                    directory = directory.getCanonicalFile();
                    if (!validateFile(directory, RepositoryType.DIR)) {
                        continue;
                    }
                    URL url = buildClassLoaderUrl(directory);
                    if (log.isDebugEnabled()) {
                        log.debug("  Including directory " + url);
                    }
                    set.add(url);
                } else if (repository.getType() == RepositoryType.JAR) {
                    File file=new File(repository.getLocation());
                    file = file.getCanonicalFile();
                    if (!validateFile(file, RepositoryType.JAR)) {
                        continue;
                    }
                    URL url = buildClassLoaderUrl(file);
                    if (log.isDebugEnabled()) {
                        log.debug("  Including jar file " + url);
                    }
                    set.add(url);
                } else if (repository.getType() == RepositoryType.GLOB) {
                    File directory=new File(repository.getLocation());
                    directory = directory.getCanonicalFile();
                    if (!validateFile(directory, RepositoryType.GLOB)) {
                        continue;
                    }
                    if (log.isDebugEnabled()) {
                        log.debug("  Including directory glob "
                            + directory.getAbsolutePath());
                    }
                    String filenames[] = directory.list();
                    if (filenames == null) {
                        continue;
                    }
                    for (String s : filenames) {
                        String filename = s.toLowerCase(Locale.ENGLISH);
                        if (!filename.endsWith(".jar")) {
                            continue;
                        }
                        File file = new File(directory, s);
                        file = file.getCanonicalFile();
                        if (!validateFile(file, RepositoryType.JAR)) {
                            continue;
                        }
                        if (log.isDebugEnabled()) {
                            log.debug("    Including glob jar file "
                                    + file.getAbsolutePath());
                        }
                        URL url = buildClassLoaderUrl(file);
                        set.add(url);
                    }
                }
            }
        }

        // Construct the class loader itself
        //根据set生成array
        final URL[] array = set.toArray(new URL[0]);
        if (log.isDebugEnabled()) {
            for (int i = 0; i < array.length; i++) {
                log.debug("  location " + i + " is " + array[i]);
            }
        }

        return AccessController.doPrivileged(
                (PrivilegedAction<URLClassLoader>) () -> {
                    if (parent == null) {
                    	//传入array,生成URLClassLoader
                        return new URLClassLoader(array);
                    } else {
                        return new URLClassLoader(array, parent);
                    }
                });
    }

接着看,我们重点关注ucp这个变量

    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();
        }
        this.acc = AccessController.getContext();
        //我们传入的array最终生成ucp
        ucp = new URLClassPath(urls, acc);
    }

最终在findClass中找到ucp的身影

    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;
    }

还记得双亲委派吗?
我们分析一下tomcat从运行,到加载commonLoader,catalinaLoader,sharedLoader这三个类的全过程。拿commonLoader举例
首先启动tomcat的bootstrap.java,交给AppClassLoader,委派给ExtClassLoader,再委派给BootStrapLoader,依次返回,最终AppClassLoader加载。然后实例化,执行我们的main方法,最终执行到initClassLoaders()方法,初始化我们的commonLoader,初始化的过程中,会把tomcat自身的资源传入到URLClassLoader类型的commonLoader中。当程序的其他地方使用我们commonLoader的时候会先执行loadClass方法委派给AppClassLoader重复上面步骤,最终执行commonLoader的findClass方法,去加载tomcat的资源。

即,commonLoader,catalinaLoader,sharedLoader没有重写loadClass方法,都没有打破双亲委派规则

我们再看WebAppClassLoader和JasperLoader

public class WebappClassLoader extends WebappClassLoaderBase {

    public WebappClassLoader() {
        super();
    }


    public WebappClassLoader(ClassLoader parent) {
        super(parent);
    }
}

我们看WebappClassLoaderBase中,重写了loadClass
并且JasperLoader中也重写了loadClass
所以二者打破了双亲委派规则

总结:

为什么WebAppClassLoader和JasperLoader要打破双亲委派规则?
1、部署在同一个Tomcat上的两个Web应用所使用的Java类库要相互隔离;
2、部署在同一个Tomcat上的两个Web应用所使用的Java类库要互相共享;
3、保证Tomcat服务器自身的安全不受部署的Web应用程序影响;
4、需要支持JSP页面的热部署和热加载;

加入我们没有打破,那么WebAppClassLoader在加载我们的web应用中的资源的时候,就会先去BootStrapClassLoader中找,再到extClassLoader中去等等,那些是tomcat本身jre中的资源,而不是web的资源,没有起到隔离的效果。加载就乱套了。所以要自定义类加载。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值