tomcat源码流程分析(一)

   本文代码基于的tomcat的源码版本为7.0.65。

 一、tomcat组件介绍

   tomcat由多个组件构成,组件的名称与之间的关系,我建议各位亲子查看tomcat的conf文件夹下的server.xml这个文件,就能有很直观的感受。这个文件中即定义了核心组件的名称,也通过xml的方式定义了包含关系。当然,这个默认文件可能包涵的组件不是很全面,但核心都包括了,如:Server,Service,Connector,Engine,Host等。

  其中Server只能有1个,1个Server可以对应多个Service,每个Service可以对应多个Connector。


 二、如何学习tomcat源码

   跟代码是学习源码的最快手段。建议下载源码后用ant编译,然后倒入eclipse,在boostrap类中开始tomcat源码之路。

   操作方法参见:http://my.oschina.net/psuyun/blog/228643?fromerr=kXXI4vdy


 三、从bootstrap开始tomcat源码之路

  1、最开始bootstrap.init();

    public void init()
        throws Exception
    {

        //设置catalina_home和catalina_base的路径。在单tomcat实例下,这2个路径是一样的。
        //如果要采用多个tomcat实例,则catalina_home路径为共享文件目录,catalina_base路径为实例独享文件目录。
        //可参见:http://blog.csdn.net/xiaohai0504/article/details/7631014
        setCatalinaHome();
        setCatalinaBase();
        //初始化类加载器,加载jar包
        initClassLoaders();

        Thread.currentThread().setContextClassLoader(catalinaLoader);

        SecurityClassLoad.securityClassLoad(catalinaLoader);

        
        if (log.isDebugEnabled())
            log.debug("Loading startup class");
        //通过放射创建Catalina类的实力,并且调用了该类的setParentClassLoade(),设置该类的类加载器为sharedLoader。
        Class<?> startupClass =
            catalinaLoader.loadClass
            ("org.apache.catalina.startup.Catalina");
        Object startupInstance = startupClass.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;
        Method method =
            startupInstance.getClass().getMethod(methodName, paramTypes);
        method.invoke(startupInstance, paramValues);

        catalinaDaemon = startupInstance;

    }
 private void initClassLoaders() {
        try {
            //commonLoader类加载器是tomcat所有类加载器的父加载器。
            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的父加载器,conf的catalina.properties并没有配置server.loder需要加载的jar包
            catalinaLoader = createClassLoader("server", commonLoader);
            //同上
            sharedLoader = createClassLoader("shared", commonLoader);
        } catch (Throwable t) {
            handleThrowable(t);
            log.error("Class loader creation threw exception", t);
            System.exit(1);
        }
    }
  private ClassLoader createClassLoader(String name, ClassLoader parent)
        throws Exception {
        //配置文件读取的是conf文件夹下的catalina.properties
        String value = CatalinaProperties.getProperty(name + ".loader");
        if ((value == null) || (value.equals("")))
            return parent;

        value = replace(value);

        List<repository> repositories = new ArrayList<repository>();

        StringTokenizer tokenizer = new StringTokenizer(value, ",");
        while (tokenizer.hasMoreElements()) {
            String repository = tokenizer.nextToken().trim();
            if (repository.length() == 0) {
                continue;
            }

            // 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));
            }
        }
        //采用URLClassLoader类来加载jar包
        return ClassLoaderFactory.createClassLoader(repositories, parent);
    }
</repository></repository>

bootstrap.init();已经完成。主要是生成了类加载器commonLoader,加载了catalina.properties中的common.loader配置的jar包。 通过反射生成了org.apache.catalina.startup.Catalina类的实例,并且设置了它的父加载器。 

   2、容器的初始化daemon.load(args);

 private void load(String[] arguments)
        throws Exception {

        // Call the load() method
        String methodName = "load";
        Object param[];
        Class<?> paramTypes[];
        if (arguments==null || arguments.length==0) {
            paramTypes = null;
            param = null;
        } else {
            paramTypes = new Class[1];
            paramTypes[0] = arguments.getClass();
            param = new Object[1];
            param[0] = arguments;
        }
        //通过反射调用Catalina实例的load()方法。
        Method method =
            catalinaDaemon.getClass().getMethod(methodName, paramTypes);
        if (log.isDebugEnabled())
            log.debug("Calling startup class " + method);
        method.invoke(catalinaDaemon, param);

    }
public void load() {

        long t1 = System.nanoTime();
        //如果没有配置catalina_home,会重新设置catalina_home的值
        initDirs();
        //设置了2个property属性javax.naming.Context.URL_PKG_PREFIXES和javax.naming.Context.INITIAL_CONTEXT_FACTORY
        initNaming();

        //使用Digester对conf/server.xml文件进行解析。Digester目前是Apache commons的一个项目。
        //这里只是创建解析的切入点,后面调用parse()方法才开始解析。
        Digester digester = createStartDigester();

        InputSource inputSource = null;
        InputStream inputStream = null;
        File file = null;
        try {
            //获取conf/server.xml
            file = configFile();
            inputStream = new FileInputStream(file);
            inputSource = new InputSource(file.toURI().toURL().toString());
        } catch (Exception e) {
            if (log.isDebugEnabled()) {
                log.debug(sm.getString("catalina.configFail", file), e);
            }
        }
        if (inputStream == null) {
            try {
                inputStream = getClass().getClassLoader()
                    .getResourceAsStream(getConfigFile());
                inputSource = new InputSource
                    (getClass().getClassLoader()
                     .getResource(getConfigFile()).toString());
            } catch (Exception e) {
                if (log.isDebugEnabled()) {
                    log.debug(sm.getString("catalina.configFail",
                            getConfigFile()), e);
                }
            }
        }

        // This should be included in catalina.jar
        // Alternative: don't bother with xml, just create it manually.
        if( inputStream==null ) {
            try {
                inputStream = getClass().getClassLoader()
                        .getResourceAsStream("server-embed.xml");
                inputSource = new InputSource
                (getClass().getClassLoader()
                        .getResource("server-embed.xml").toString());
            } catch (Exception e) {
                if (log.isDebugEnabled()) {
                    log.debug(sm.getString("catalina.configFail",
                            "server-embed.xml"), e);
                }
            }
        }


        if (inputStream == null || inputSource == null) {
            if  (file == null) {
                log.warn(sm.getString("catalina.configFail",
                        getConfigFile() + "] or [server-embed.xml]"));
            } else {
                log.warn(sm.getString("catalina.configFail",
                        file.getAbsolutePath()));
                if (file.exists() && !file.canRead()) {
                    log.warn("Permissions incorrect, read permission is not allowed on the file.");
                }
            }
            return;
        }

        try {
            inputSource.setByteStream(inputStream);
            //开始解析xml
            digester.push(this);
            digester.parse(inputSource);
        } catch (SAXParseException spe) {
            log.warn("Catalina.start using " + getConfigFile() + ": " +
                    spe.getMessage());
            return;
        } catch (Exception e) {
            log.warn("Catalina.start using " + getConfigFile() + ": " , e);
            return;
        } finally {
            try {
                inputStream.close();
            } catch (IOException e) {
                // Ignore
            }
        }
        //Digester中设置了当前server为StandardServer
        getServer().setCatalina(this);

        // Stream redirection
        initStreams();

        // Start the new server
        try {
            //初始化Server,init()方法为LifeCycleBase的方法,Service,Connector等组件都集成了这个类,初始化的时候都用这个方法。
            //采用的是模版方法
            getServer().init();
        } catch (LifecycleException e) {
            if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE")) {
                throw new java.lang.Error(e);
            } else {
                log.error("Catalina.start", e);
            }

        }

        long t2 = System.nanoTime();
        if(log.isInfoEnabled()) {
            log.info("Initialization processed in " + ((t2 - t1) / 1000000) + " ms");
        }

    }
 public final synchronized void init() throws LifecycleException {
        if (!state.equals(LifecycleState.NEW)) {
            invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
        }
        //设置组件的状态为正在初始化。这里还用到了观察者模式,Server.xml中配置了很多Listener,Listener会注册到各个组件中,
        //当组件的状态改变的时候,就会遍历组件的listeners。
        setStateInternal(LifecycleState.INITIALIZING, null, false);

        try {
            //每个组件自己的初始化
            initInternal();
        } catch (Throwable t) {
            ExceptionUtils.handleThrowable(t);
            setStateInternal(LifecycleState.FAILED, null, false);
            throw new LifecycleException(
                    sm.getString("lifecycleBase.initFail",toString()), t);
        }
        //设置组件的状态为初始化完成
        setStateInternal(LifecycleState.INITIALIZED, null, false);
    }
    
init()主要是对所有组件进行初始化,先从Server开始,1个Server又可能包涵多个Service,1个Service可能包涵多个Connctor,直到所有的组件都初始化完成。每个组件初始化前后,会更改当前组件的状态,对该组件进行监听的对象将根据组件状态确认是否要有所作用,这就是观察者模式。3、容器的启动。daemon.start();
        public void start() {
        //没有Server就重新初始化。
        if (getServer() == null) {
            load();
        }

        if (getServer() == null) {
            log.fatal("Cannot start server. Server instance is not configured.");
            return;
        }

        long t1 = System.nanoTime();

        //
        try {
            getServer().start();
        } catch (LifecycleException e) {
            log.fatal(sm.getString("catalina.serverStartFail"), e);
            try {
                getServer().destroy();
            } catch (LifecycleException e1) {
                log.debug("destroy() failed for failed Server ", e1);
            }
            return;
        }

        long t2 = System.nanoTime();
        if(log.isInfoEnabled()) {
            log.info("Server startup in " + ((t2 - t1) / 1000000) + " ms");
        }

        // Register shutdown hook
        if (useShutdownHook) {
            if (shutdownHook == null) {
                shutdownHook = new CatalinaShutdownHook();
            }
            Runtime.getRuntime().addShutdownHook(shutdownHook);

            // If JULI is being used, disable JULI's shutdown hook since
            // shutdown hooks run in parallel and log messages may be lost
            // if JULI's hook completes before the CatalinaShutdownHook()
            LogManager logManager = LogManager.getLogManager();
            if (logManager instanceof ClassLoaderLogManager) {
                ((ClassLoaderLogManager) logManager).setUseShutdownHook(
                        false);
            }
        }

        if (await) {
            await();
            stop();
        }
    }
这里和容器的初始化有异曲同工之妙,都是采用模版方式,通过调用公共接口的实现方法start(),然后在start()中调用各自的startInternal();其中还包含着状态改变而通知监听者的观察者模式。 到这里,bootstrap的main()方法的主要流程已经结束了,分为3步:初始化类加载器,初始化组件,启动组件。其中用到了模版模式和观察者模式。后续我将继续介绍tomcat中各个组件是如何工作的。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值