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.创建了三个类加载器,commonLoader\catalinaLoader\sharedLoader
类加载器:加载类文件的类
所以创建三个类加载器,就是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类

posted @ 2019-12-31 18:10  楠予  阅读(275)  评论(0编辑  收藏  举报