tomcat源码解析--Loader

tomcat 如何加载 class 文件? 如何加载 lib 目录下的jar 文件?

在 tomcat 中存在一个 Loader接口:



 其中的主要功能包括



 container: Loader 是与一个context关联的,负责在context下的所有servlet解析的时候使用

可以设置reloadable参数,repository是指需要加载的资源的路径

其子类 WebappLoader 负责了以上方法的实现,并且实现了其他一些接口(implements Lifecycle, Loader, PropertyChangeListener, MBeanRegistration )

具体的解析任务将会交给 webappclassLoader类

在tomcat的classLoader运作机制中,主要将资源分为3层

顶层是 webapp的classes 资源和lib 目录下的 jar 资源

第二层是 tomcat 自己lib目录下的资源

底层是 java jdk下的lib资源

当容器根据一个url寻找资源时,默认是从顶层开始。


 

 

WebappLoader的 主要 任务(start方法):

1. 创建classLoader

 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默认使用 WebappClassLoader

 2. 配置 classLoader:

 

            classLoader.setResources(container.getResources());
            classLoader.setDelegate(this.delegate);
            classLoader.setSearchExternalFirst(searchExternalFirst);
            if (container instanceof StandardContext) {
                classLoader.setAntiJARLocking(
                        ((StandardContext) container).getAntiJARLocking());
                classLoader.setClearReferencesStopThreads(
                        ((StandardContext) container).getClearReferencesStopThreads());
                classLoader.setClearReferencesStopTimerThreads(
                        ((StandardContext) container).getClearReferencesStopTimerThreads());
                classLoader.setClearReferencesThreadLocals(
                        ((StandardContext) container).getClearReferencesThreadLocals());
                classLoader.setClearReferencesHttpClientKeepAliveThread(
                        ((StandardContext) container).getClearReferencesHttpClientKeepAliveThread());
            }
 关于container.getResources() 对象 DirContext 将在之后分析

antiJARLocking 属性, 在classLoader. getResource方法中可见

clearReferencesStopThreads 见 classLoader.clearReferences -> clearReferencesThreads方法(关闭web app start的任何线程)

clearReferencesStopTimerThreads 见  classLoader.clearReferences -> clearReferencesThreads方法 用于 判断是否调用 clearReferencesStopTimerThread 方法, 默认 false

clearReferencesThreadLocals 见 clearReferences 方法

clearReferencesHttpClientKeepAliveThread  见 clearReferences  方法

3. 加载资源

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

4. 加载class文件和lib路径:

setRepositories 方法

4.1  设置默认工作路径:

        if (!(container instanceof Context))
            return;
        ServletContext servletContext =
            ((Context) container).getServletContext();
        if (servletContext == null)
            return;

        loaderRepositories=new ArrayList();
        // Loading the work directory
        File workDir =
            (File) servletContext.getAttribute(Globals.WORK_DIR_ATTR);
        if (workDir == null) {
            log.info("No work dir for " + servletContext);
        }

        if( log.isDebugEnabled()) 
            log.debug(sm.getString("webappLoader.deploy", workDir.getAbsolutePath()));

        classLoader.setWorkDir(workDir);

 4.2 得到classes 文件夹路径 资源

String classesPath = "/WEB-INF/classes";
        DirContext classes = null;

        try {
            Object object = resources.lookup(classesPath);
            if (object instanceof DirContext) {
                classes = (DirContext) object;
            }
        } catch(NamingException e) {
            // Silent catch: it's valid that no /WEB-INF/classes collection
            // exists
        }

        if (classes != null) {

            File classRepository = null;

            String absoluteClassesPath =
                servletContext.getRealPath(classesPath);

            if (absoluteClassesPath != null) {

                classRepository = new File(absoluteClassesPath);

            } else {

                classRepository = new File(workDir, classesPath);
                classRepository.mkdirs();
                if (!copyDir(classes, classRepository)) {
                    throw new IOException(
                            sm.getString("webappLoader.copyFailure"));
                }

            }

            if(log.isDebugEnabled())
                log.debug(sm.getString("webappLoader.classDeploy", classesPath,
                             classRepository.getAbsolutePath()));


            // Adding the repository to the class loader
            classLoader.addRepository(classesPath + "/", classRepository);
            loaderRepositories.add(classesPath + "/" );

        }

 并且 把 相关的 classPath classRepository 设置到 classLoader中

4.3  lib 中的jar资源导入

 String libPath = "/WEB-INF/lib";

        classLoader.setJarPath(libPath);

        DirContext libDir = null;
        // Looking up directory /WEB-INF/lib in the context
        try {
            Object object = resources.lookup(libPath);
            if (object instanceof DirContext)
                libDir = (DirContext) object;
        } catch(NamingException e) {
            // Silent catch: it's valid that no /WEB-INF/lib collection
            // exists
        }

        if (libDir != null) {

            boolean copyJars = false;
            String absoluteLibPath = servletContext.getRealPath(libPath);

            File destDir = null;

            if (absoluteLibPath != null) {
                destDir = new File(absoluteLibPath);
            } else {
                copyJars = true;
                destDir = new File(workDir, libPath);
                destDir.mkdirs();
            }

            // Looking up directory /WEB-INF/lib in the context
            NamingEnumeration<NameClassPair> enumeration = null;
            try {
                enumeration = libDir.list("");
            } catch (NamingException e) {
                IOException ioe = new IOException(sm.getString(
                        "webappLoader.namingFailure", libPath));
                ioe.initCause(e);
                throw ioe;
            }
                while (enumeration.hasMoreElements()) {
                NameClassPair ncPair = enumeration.nextElement();
                String filename = libPath + "/" + ncPair.getName();
                    if (!filename.endsWith(".jar"))
                        continue;

                    // Copy JAR in the work directory, always (the JAR file
                    // would get locked otherwise, which would make it
                    // impossible to update it or remove it at runtime)
                File destFile = new File(destDir, ncPair.getName());

                    if( log.isDebugEnabled())
                    log.debug(sm.getString("webappLoader.jarDeploy", filename,
                                     destFile.getAbsolutePath()));

                // Bug 45403 - Explicitly call lookup() on the name to check
                // that the resource is readable. We cannot use resources
                // returned by listBindings(), because that lists all of them,
                // but does not perform the necessary checks on each.
                Object obj = null;
                try {
                    obj = libDir.lookup(ncPair.getName());
                } catch (NamingException e) {
                    IOException ioe = new IOException(sm.getString(
                            "webappLoader.namingFailure", filename));
                    ioe.initCause(e);
                    throw ioe;
                }
                    
                    if (!(obj instanceof Resource))
                        continue;
                    
                    Resource jarResource = (Resource) obj;
                    
                    if (copyJars) {
                        if (!copy(jarResource.streamContent(),
                              new FileOutputStream(destFile))) {
                        throw new IOException(
                                sm.getString("webappLoader.copyFailure"));
                    }
                }

                    try {
                        JarFile jarFile = new JarFile(destFile);
                        classLoader.addJar(filename, jarFile, destFile);
                    } catch (Exception ex) {
                        // Catch the exception if there is an empty jar file
                    // Should ignore and continue loading other jar files 
                        // in the dir
                    }
                    
                    loaderRepositories.add( filename );

5. 设置 classpath, 设置webapp的环境变量

 

// Validate our current state information
        if (!(container instanceof Context))
            return;
        ServletContext servletContext =
            ((Context) container).getServletContext();
        if (servletContext == null)
            return;

        if (container instanceof StandardContext) {
            String baseClasspath = 
                ((StandardContext) container).getCompilerClasspath();
            if (baseClasspath != null) {
                servletContext.setAttribute(Globals.CLASS_PATH_ATTR,
                                            baseClasspath);
                return;
            }
        }

        StringBuffer classpath = new StringBuffer();

        // Assemble the class path information from our class loader chain
        ClassLoader loader = getClassLoader();
        int layers = 0;
        int n = 0;
        while (loader != null) {
            if (!(loader instanceof URLClassLoader)) {
                String cp=getClasspath( loader );
                if( cp==null ) {
                    log.info( "Unknown loader " + loader + " " + loader.getClass());
                    break;
                } else {
                    if (n > 0) 
                        classpath.append(File.pathSeparator);
                    classpath.append(cp);
                    n++;
                }
                break;
                //continue;
            }
            URL repositories[] =
                ((URLClassLoader) loader).getURLs();
            for (int i = 0; i < repositories.length; i++) {
                String repository = repositories[i].toString();
                if (repository.startsWith("file://"))
                    repository = repository.substring(7);
                else if (repository.startsWith("file:"))
                    repository = repository.substring(5);
                else if (repository.startsWith("jndi:"))
                    repository =
                        servletContext.getRealPath(repository.substring(5));
                else
                    continue;
                if (repository == null)
                    continue;
                if (n > 0)
                    classpath.append(File.pathSeparator);
                classpath.append(repository);
                n++;
            }
            loader = loader.getParent();
            layers++;
        }

        this.classpath=classpath.toString();

        // Store the assembled class path as a servlet context attribute
        servletContext.setAttribute(Globals.CLASS_PATH_ATTR,
                                    classpath.toString());

 通过循环将各个loader中的资源路径加载到classpath 变量中

 

6. setPermissions 设置资源 权限 

判断是否 存在 SecurityManager 

if (!Globals.IS_SECURITY_ENABLED)
            return;
        if (!(container instanceof Context))
            return;

 只允许根目录的读写权限,和classes 、lib目录的读写删权限,其他根目录下的资源不能碰

 URL rootURL = servletContext.getResource("/");
            classLoader.addPermission(rootURL);

            String contextRoot = servletContext.getRealPath("/");
            if (contextRoot != null) {
                try {
                    contextRoot = (new File(contextRoot)).getCanonicalPath();
                    classLoader.addPermission(contextRoot);
                } catch (IOException e) {
                    // Ignore
                }
            }

 

URL classesURL = servletContext.getResource("/WEB-INF/classes/");
            classLoader.addPermission(classesURL);
            URL libURL = servletContext.getResource("/WEB-INF/lib/");
            classLoader.addPermission(libURL);

 7. 启动 classloader, 绑定 JMX

if (classLoader instanceof Lifecycle)
                ((Lifecycle) classLoader).start();

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

            StandardContext ctx=(StandardContext)container;
            Engine eng=(Engine)ctx.getParent().getParent();
            String path = ctx.getPath();
            if (path.equals("")) {
                path = "/";
            }   
            ObjectName cloname = new ObjectName
                (ctx.getEngineName() + ":type=WebappClassLoader,path="
                 + path + ",host=" + ctx.getParent().getName());
            Registry.getRegistry(null, null)
                .registerComponent(classLoader, cloname, null);

 

 

 




 

猜你喜欢

转载自patrick002.iteye.com/blog/2145963