zoukankan      html  css  js  c++  java
  • Tomcat源码阅读(初窥)

    第一次工作的时候,公司项目就是用Tomcat在来运行项目的,用于小项目服务,启动快,使用方便。
    Tomcat是一个服务器项目,由Apache组织维护,开源代码;
    tomcat连接前端页面与后端,将一个个浏览器的请求发送给server,server接收请求处理完再放回给前端处理结果
    这一步步是怎么做到的,肯定是不一般的设计,因此就有想对Tomcat深入了解的想法。

    Tomcat的启动运行

    启动方式

    windows系统环境启动

    startup.bat  ## 运行startup.bat 文件
    

    linux系统环境启动

    netstat -tnlp # 查看网络端口占用情况
    
    startup.sh  # 运行 startup.sh脚本文件
    

    启动脚本

    1.确定java的jre环境 JAVA_HOME
    2.确定tomcat的家,默认跟路径 CATALINA_HOME
    3.用java命令运行Bootstrap.java 的main方法,设置全局变量,启动Tomcat

    启动类-BootStrap.java

    tomcat服务器的启动类,main方法中启动tomcat
    BootStrap类的main方法中主要做了:
    1.初始化类加载器
    2.设置tomcat上下文类加载器
    3.创建Catalina实例
    4.调用catalina的load方法初始化lifecycle等基础组件
    5.调用catalina的start方法启动tomcat

    tomcat的设计定位,做了很多的特别的设计,如类加载的自定义,类加载机制并非双亲委派机制,上下文加载器的自定义,生命周期类组件定义,方便服务的管理

    main方法

    get

    代码:

    /**
         * Main method and entry point when starting Tomcat via the provided
         * scripts.
         *
         * @param args Command line arguments to be processed
         */
        public static void main(String args[]) {
    
            if (daemon == null) {
                // Don't set daemon until init() has completed
                Bootstrap bootstrap = new Bootstrap();
                try {
                    bootstrap.init();
                } catch (Throwable t) {
                    handleThrowable(t);
                    t.printStackTrace();
                    return;
                }
                daemon = bootstrap;
            } else {
                // When running as a service the call to stop will be on a new
                // thread so make sure the correct class loader is used to prevent
                // a range of class not found exceptions.
                Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);
            }
    
            try {
                String command = "start";
                if (args.length > 0) {
                    command = args[args.length - 1];
                }
    
                if (command.equals("startd")) {
                    args[args.length - 1] = "start";
                    daemon.load(args);
                    daemon.start();
                } else if (command.equals("stopd")) {
                    args[args.length - 1] = "stop";
                    daemon.stop();
                } else if (command.equals("start")) {
                    daemon.setAwait(true);
                    daemon.load(args);
                    daemon.start();
                    if (null == daemon.getServer()) {
                        System.exit(1);
                    }
                } else if (command.equals("stop")) {
                    daemon.stopServer(args);
                } else if (command.equals("configtest")) {
                    daemon.load(args);
                    if (null == daemon.getServer()) {
                        System.exit(1);
                    }
                    System.exit(0);
                } else {
                    log.warn("Bootstrap: command "" + command + "" does not exist.");
                }
            } catch (Throwable t) {
                // Unwrap the Exception for clearer error reporting
                if (t instanceof InvocationTargetException &&
                        t.getCause() != null) {
                    t = t.getCause();
                }
                handleThrowable(t);
                t.printStackTrace();
                System.exit(1);
            }
    
        }
    

    1.判断deamon 是否为空,为空则需要初始化

    Bootstrap bootstrap = new Bootstrap();
    try {
    	bootstrap.init();
    } catch (Throwable t) {
    	handleThrowable(t);
    	t.printStackTrace();
    	return;
    }
    daemon = bootstrap;
    

    2.根据启动命令参数决定 deamon的下一步动作

    String command = "start";
    if (args.length > 0) {
        command = args[args.length - 1];
    }
    

    3.load为启动做准备,然后start开始运行
    很明显,如果启动命令没有任何参数, Tomcat直接默认启动

    else if (command.equals("start")) {
    	daemon.setAwait(true);
    	daemon.load(args);
    	daemon.start();
    	if (null == daemon.getServer()) {
    		System.exit(1);
    	}
    }
    

    init() 类加载初始设置

    /**
     * Initialize daemon.
     * @throws Exception Fatal initialization error
     */
    public void init() throws Exception {
    
    	initClassLoaders();
    
    	Thread.currentThread().setContextClassLoader(catalinaLoader);
    
    	SecurityClassLoad.securityClassLoad(catalinaLoader);
    
    	// 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;
    	Method method =
    		startupInstance.getClass().getMethod(methodName, paramTypes);
    	method.invoke(startupInstance, paramValues);
    
    	catalinaDaemon = startupInstance;
    
    }
    
    初始化类加载器

    initClassLoaders();
    初始化 Tomcat自己定义的三种类加载器

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

    在初始化的时候,
    1.创建了三个类加载器,commonLoadercatalinaLoadersharedLoader
    类加载器:加载类文件的类
    所以创建三个类加载器,就是Tomcat要实现对类文件进行一套自己的管理方案的具体方法(区别于Java默认的类文件加载)
    2.设置线程上下文类加载器Thread.currentThread().setContextClassLoader(catalinaLoader);
    上下文类加器

    配置类加载环境

    上写文类加载器
    Thread.currentThread().setContextClassLoader(catalinaLoader);
    设置自己的安全类加载器
    SecurityClassLoad.securityClassLoad(catalinaLoader);

    创建CataLina实例(org.apache.catalina.startup.Catalina)
    // 1.反射创建CataLina对象
    Class<?> startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");
    Object startupInstance = startupClass.getConstructor().newInstance();
    
    // 2.反射获取setParentClassLoader方法对象
    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);
    // 3.反射设置Catalina的父类加载器为sharedLoader
    method.invoke(startupInstance, paramValues);
    
    // 4.将Catalina赋值给CatalinaDaemon 用于后续服务启动关闭重启的关键对象
    catalinaDaemon = startupInstance;
    

    daemon.load(args) 初始化Tomcat各组件(生命周期为主)

    /**
     * Load daemon.
     */
    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;
    	}
    	Method method =
    		catalinaDaemon.getClass().getMethod(methodName, paramTypes);
    	if (log.isDebugEnabled())
    		log.debug("Calling startup class " + method);
    	method.invoke(catalinaDaemon, param);
    
    }
    

    Catalina

    load() 方法初始化Tomcat组件

    BootStrap.java 类中 daemon.load(args) 方法最后通过反射调用了catalina实例对象的load方法
    主要就是通过反射执行catalina对象的load方法(org.apache.catalina.startup.Catalina),load代码有点多,稍微了解下:

    /**
     * Start a new server instance.
     */
    public void load() {
    
    	if (loaded) {
    		return;
    	}
    	loaded = true;
    
    	long t1 = System.nanoTime();
    
    	initDirs();
    
    	// Before digester - it may be needed
    	initNaming();
            // 1.创建Digester对象,用于后面的解析Server.xml 文件,创建StandardServer等生命周期对象
    	// Create and execute our Digester
    	Digester digester = createStartDigester();
    
    	InputSource inputSource = null;
    	InputStream inputStream = null;
    	File file = null;
    	try {
    		try {
    			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);
    			digester.push(this); // 2.cataLina与digester关联,设置cataLina为root
    			digester.parse(inputSource); // 3 解析文件并在这里创建server listener  Connector Executor的实例
    		} catch (SAXParseException spe) {
    			log.warn("Catalina.start using " + getConfigFile() + ": " +
    					spe.getMessage());
    			return;
    		} catch (Exception e) {
    			log.warn("Catalina.start using " + getConfigFile() + ": " , e);
    			return;
    		}
    	} finally {
    		if (inputStream != null) {
    			try {
    				inputStream.close();
    			} catch (IOException e) {
    				// Ignore
    			}
    		}
    	}
    
    	getServer().setCatalina(this); // 4.获取StandardServer对象,并与catalina关联
    	getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile());
    	getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile());
    
    	// Stream redirection
    	initStreams();
    
    	// Start the new server
    	try {
    		getServer().init(); // 5.standardServer初始化,下面一行代码是LifecycleException,说明方法与生命周期初始化有关 
    	} 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"); // 6.打印记录Tomcat启动时初始化的时间
    	}
    }
    

    daemon.start();

    /**
     * Start a new server instance.
     */
    public void start() {
    
    	if (getServer() == null) {
    		load(); //1 判断是否创建Server实例 , 没有调用load方法试图创建Server实例
    	}
    
    	if (getServer() == null) {
    		log.fatal("Cannot start server. Server instance is not configured.");
    		return;
    	}
    
    	long t1 = System.nanoTime();
    
    	// Start the new server
    	try {
    		getServer().start(); // 2.调用server的start()方法,这里是Server接口的实现类:StandardServer的实例
    	} 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"); //3 日志打印tomcat启动时长
    	}
    
    	// 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();
    	}
    }
    

    Tomcat类

  • 相关阅读:
    分层图最短路(DP思想) BZOJ2662 [BeiJing wc2012]冻结
    动态规划 BZOJ1925 地精部落
    线性DP SPOJ Mobile Service
    线性DP codevs2185 最长公共上升子序列
    数位DP POJ3208 Apocalypse Someday
    线性DP POJ3666 Making the Grade
    杨氏矩阵 线性DP? POJ2279 Mr.Young's Picture Permutations
    tarjan强连通分量 洛谷P1262 间谍网络
    树链剖分 BZOJ3589 动态树
    二分图 BZOJ4554 [Tjoi2016&Heoi2016]游戏
  • 原文地址:https://www.cnblogs.com/Narule/p/11979365.html
Copyright © 2011-2022 走看看