Tomcat类加载

Tomcat的类加载设计
类载入:1 new隐式载入,2 显示载入loadclass()

我们都知道:Java默认的类加载机制是委派模式,委派的过程如下。

1)从缓存中加载
2) 如果没有,则从父加载器中加载
3) 如果父加载器没有,从当前加载器加载
4) 没有,抛异常
servlet容器不应该完全信任它正在运行的servlet类。 servlet应该只允许访问web-inf/class和web-inf/lib中的类。
而tomcat自己定义加载器后,可以加入限定规则。

下面是ClassLoader的loadClass()方法源码逻辑

 protected Class<?> loadClass(String name, boolean resolve)
    {
        synchronized (getClassLoadingLock(name)) {
        //1 查看是否加载过 
            Class<?> c = findLoadedClass(name);//
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    if (parent != null) {
                    //2 父加载器加载
                        c = parent.loadClass(name, false);
                    } else { //3 系统加载器加载
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                }
                if (c == null) {
                    long t1 = System.nanoTime();
                    //4自定义的加载器 加载
                    c = findClass(name);
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }
复制代码

上图是tomcat的类加载器继承图,顶层设计在loader接口。loader接口的setContainer方法将载入器和servlet容器关联。如果context容器的类被修改了,载入器也可以支持对类的自动重载。载入器会调用context接口的reloader方法来载入。WebappLoader中还包含一个WebappClassLoader,该类是默认的类加载器。当webappLoader随着生命周期启动时,会调用内部createClassLoader()方法来创建一个默认的类加载器。

    private WebappClassLoader createClassLoader() throws Exception {

        Class<?> clazz = Class.forName(loaderClass);
        WebappClassLoader classLoader = null;

        if (parentClassLoader == null) {
            parentClassLoader = container.getParentClassLoader();
        }
        Class<?>[] argTypes = { ClassLoader.class };
        Object[] args = { parentClassLoader };
        Constructor<?> constr = clazz.getConstructor(argTypes);
        classLoader = (WebappClassLoader) constr.newInstance(args);

        return classLoader;

    }
复制代码

设置仓库(即设置在哪加载类)
上面拿到classLoader后,会调用classLoader.addRepository(repositories[i])内部调用WebappClassLoader# addRepository(String repository)设置仓库。

 public void addRepository(String repository) {
        if (repository.startsWith("/WEB-INF/lib")
            || repository.startsWith("/WEB-INF/classes"))
            return;
        try {
            URL url = new URL(repository);
            super.addURL(url);
            hasExternalRepositories = true;
            repositoryURLs = null;
        } catch (MalformedURLException e) {
         
        }

    }
复制代码

这是整个载入过程(省略部分不重要代码)

protected void startInternal() throws LifecycleException {
        URLStreamHandlerFactory streamHandlerFactory =
                DirContextURLStreamHandlerFactory.getInstance();
        if (first) {
            first = false;
            try {
                URL.setURLStreamHandlerFactory(streamHandlerFactory);
            } catch (Exception e) {
           
            } catch (Throwable t) {
      
            }
        }

        // Construct a class loader based on our current repositories list
        try {

            classLoader = createClassLoader();
            classLoader.setResources(container.getResources());
            classLoader.setDelegate(this.delegate);
            classLoader.setSearchExternalFirst(searchExternalFirst);
            if (container instanceof StandardContext) {
                classLoader.setAntiJARLocking(
                        ((StandardContext) container).getAntiJARLocking());
                classLoader.setClearReferencesStatic(
                        ((StandardContext) container).getClearReferencesStatic());
                classLoader.setClearReferencesStopThreads(
                        ((StandardContext) container).getClearReferencesStopThreads());
                classLoader.setClearReferencesStopTimerThreads(
                        ((StandardContext) container).getClearReferencesStopTimerThreads());
                classLoader.setClearReferencesHttpClientKeepAliveThread(
                        ((StandardContext) container).getClearReferencesHttpClientKeepAliveThread());
            }

            for (int i = 0; i < repositories.length; i++) {
                classLoader.addRepository(repositories[i]);
            }

            // Configure our repositories
            setRepositories();//--设置仓库
            setClassPath();//--设置类路径

            setPermissions();//--借用安全管理器,设置类载入器对路径的访问权限

            ((Lifecycle) classLoader).start();

            // Binding the Webapp class loader to the directory context
            DirContextURLStreamHandler.bind(classLoader,
                    this.container.getResources());

            StandardContext ctx=(StandardContext)container;
            String contextName = ctx.getName();
            if (!contextName.startsWith("/")) {
                contextName = "/" + contextName;
            }
            ObjectName cloname = new ObjectName
                (MBeanUtils.getDomain(ctx) + ":type=WebappClassLoader,context="
                 + contextName + ",host=" + ctx.getParent().getName());
            Registry.getRegistry(null, null)
                .registerComponent(classLoader, cloname, null);

        } catch (Throwable t) {
        

        setState(LifecycleState.STARTING);
    }
复制代码

tomcat中正在干活的类加载器是WebappClassLoader类。该类的设计考虑到了优化和安全两个方面。例如:会缓存以前已经加载过的类,同时,还会缓存加载失败的类的名字。WebappClassLoader会在仓库列表和指定的JAR文件搜索。

WebappClassLoader不允许载入指定的某些类,名字存在triggers和packageTriggers

那么,它是如何完成类的缓存和载入任务的呢?

public Class loadClass(String name, boolean resolve) throws ClassNotFoundException {
        if (debug >= 2)
            log("loadClass(" + name + ", " + resolve + ")");
        Class clazz = null;

        // Don't load classes if class loader is stopped
        if (!started) {
            log("Lifecycle error : CL stopped");
            throw new ClassNotFoundException(name);
        }

        // (0) Check our previously loaded local class cache
        clazz = findLoadedClass0(name);//先在本地找,看是否加载过.(根据类名找类)
        if (clazz != null) {
            if (debug >= 3)
                log("  Returning class from cache");
            if (resolve)
                resolveClass(clazz);
            return (clazz);
        }

        // (0.1) Check our previously loaded class cache
        clazz = findLoadedClass(name);//看上一级 即classloader是否加载过
        if (clazz != null) {
            if (debug >= 3)
                log("  Returning class from cache");
            if (resolve)
                resolveClass(clazz);
            return (clazz);
        }

        // (0.2) Try loading the class with the system class loader, to prevent
        //       the webapp from overriding J2SE classes
        try {//--使用系统的类加载器进行加载
            clazz = system.loadClass(name);
            if (clazz != null) {
                if (resolve)
                    resolveClass(clazz);
                return (clazz);
            }
        } catch (ClassNotFoundException e) {
            // Ignore
        }

        // (0.5) Permission to access this class when using a SecurityManager
        if (securityManager != null) {
            int i = name.lastIndexOf('.');
            if (i >= 0) {
                try {//--检查该类是否允许被加载
                    securityManager.checkPackageAccess(name.substring(0,i));
                } catch (SecurityException se) {
                    String error = "Security Violation, attempt to use " + "Restricted Class: " + name;
                    System.out.println(error);
                    se.printStackTrace();
                    log(error);
                    throw new ClassNotFoundException(error);
                }
            }
        }

        boolean delegateLoad = delegate || filter(name);

        // (1) Delegate to our parent if requested
        if (delegateLoad) {
            if (debug >= 3)
                log("  Delegating to parent classloader");
            ClassLoader loader = parent;
            if (loader == null)
                loader = system;
            try {
                clazz = loader.loadClass(name);
                if (clazz != null) {
                    if (debug >= 3)
                        log("  Loading class from parent");
                    if (resolve)
                        resolveClass(clazz);
                    return (clazz);
                }
            } catch (ClassNotFoundException e) {
                ;
            }
        }

        // (2) Search local repositories
        if (debug >= 3)
            log("  Searching local repositories");
        try {
            clazz = findClass(name);
            if (clazz != null) {
                if (debug >= 3)
                    log("  Loading class from local repository");
                if (resolve)
                    resolveClass(clazz);
                return (clazz);
            }
        } catch (ClassNotFoundException e) {
            ;
        }

        // (3) Delegate to parent unconditionally
        if (!delegateLoad) {
            if (debug >= 3)
                log("  Delegating to parent classloader");
            ClassLoader loader = parent;
            if (loader == null)
                loader = system;
            try {
                clazz = loader.loadClass(name);
                if (clazz != null) {
                    if (debug >= 3)
                        log("  Loading class from parent");
                    if (resolve)
                        resolveClass(clazz);
                    return (clazz);
                }
            } catch (ClassNotFoundException e) {
                ;
            }
        }

        // This class was not found
        throw new ClassNotFoundException(name);

    }
复制代码

转载于:https://juejin.im/post/5b3edb5f5188251aa0162176

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值