Tomcat 8.5 源码分析

2023-09-24 21:46
一、获取源码并启动程序 获取教程地址 总体架构 二、Tomcat的启动入口 Catalina类主要负责 具体的管理类,而Bootstrap类是启动的入口(main方法)。 public static void main(String args[]) {synchronized (daemonLock) {if (daemon == null) {Bootstrap bootstrap = new Bootstrap();try {bootstrap.init();} catch (Throwable t) {handleThrowable(t);t.printStackTrace();return;}daemon = bootstrap;} else {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) {if (t instanceof InvocationTargetException &&t.getCause() != null) {t = t.getCause();}handleThrowable(t);t.printStackTrace();System.exit(1);}} 2.1 Bootstrap#init() public void init() throws Exception {initClassLoaders();Thread.currentThread().setContextClassLoader(catalinaLoader);SecurityClassLoad.securityClassLoad(catalinaLoader);if (log.isDebugEnabled()) {log.debug("Loading startup class");}Class startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");Object startupInstance = startupClass.getConstructor().newInstance();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;} 关键步骤 初始化类加载器设置当前线程的上下文加载器为catalinaLoader预先加载tomcat、javax包的自定义类通过反射创建Catalina对象将startupInstance对象赋值给catalinaDaemon 2.2 Bootstrap#init()#initClassLoaders() init()初始化的方法也相对的简单,首先调用initClassLoaders()初始化类加载器,使得tomcat可以加载应用程序类,接着设置当前线程的上下文加载器为CatalinaLoader。 private void initClassLoaders() {try {commonLoader = createClassLoader("common", null);if (commonLoader == null) {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);}} 在initClassLoaders()初始化方法中可发现会创建三种类加载器并赋予成员变量,其中catalinaLoader与sharedLoader加载器的父加载器都是commonLoader。 2.3 Catalina#load() load()方法主要完成StardardServer及子组件的初始化,下图是该方法的主要序列流程: public void load() {if (loaded) {return;}loaded = true;long t1 = System.nanoTime();initDirs();initNaming();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);}} try {inputSource.setByteStream(inputStream);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 {if (inputStream != null) {try {inputStream.close();} catch (IOException e) {}}}getServer().setCatalina(this);getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile());getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile());initStreams();try {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);}}} 2.4 standerServer#initInternal() 在执行LifecycleBase#initInternal()抽象方法时,将由standerServer#initInternal()的方法完成初始化。 @Overrideprotected void initInternal() throws LifecycleException {super.initInternal();onameStringCache = register(new StringCache(), "type=StringCache");MBeanFactory factory = new MBeanFactory();factory.setContainer(this);onameMBeanFactory = register(factory, "type=MBeanFactory");globalNamingResources.init();for (Service service : services) {service.init();}} 而service组件的init()方法主要做了以下事情: 初始化StandardEngine容器。 创建指定或默认的Realm。 初始化绑定的StandardThreadExecutor线程执行器。初始化绑定的MapperListener监听器。更新mapper监听器的生命周期状态为LifecycleState.STARTING。查找并绑定默认的虚拟主机Host。绑定该监听器到Engine及子容器。注册Engine容器到绑定的虚拟主机Host以及上下文Context、Wrapper。初始化绑定的所有connector连接器。 @Overrideprotected void initInternal() throws LifecycleException {super.initInternal();if (engine != null) {engine.init();}for (Executor executor : findExecutors()) {if (executor instanceof JmxEnabled) {((JmxEnabled) executor).setDomain(getDomain());}executor.init();}mapperListener.init();synchronized (connectorsLock) {for (Connector connector : connectors) {try {connector.init();} catch (Exception e) {String message = sm.getString("standardService.connector.initFailed", connector);log.error(message, e);if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE")) {throw new LifecycleException(message);}}}}} 在整个service初始化过程中,connector连接器的初始化尤其重要,以下为connector连接器初始化代码: 创建、初始化Coyote适配器,并绑定APR或HTTP1.1协议处理器。检查parseBodyMethodsSet是否有值,若没则设置为POST方法。判断APR的native库是否必须且协议处理器实例是否已创建,若都不满足则抛出异常。判断APR本地库是否必须且APR协议处理器是否可用,若都不满足则抛出异常。判断APR协议处理器是否可用且Apr协议处理器是否使用OpenSSL且协议处理器是否AbstractHttp11JsseProtocol类型。 判断是否启用SSL且其类名为空,若满足则使用OpenSSLImplementation类名(OpenSSL与JSSE的配置兼容)。否则执行步骤6 初始化协议处理器(该协议处理器实例时在server.xml解析时创建的)。 @Override protected void initInternal() throws LifecycleException {super.initInternal();adapter = new CoyoteAdapter(this);protocolHandler.setAdapter(adapter);if (null == parseBodyMethodsSet) {setParseBodyMethods(getParseBodyMethods());}if (protocolHandler.isAprRequired() && !AprLifecycleListener.isInstanceCreated()) {throw new LifecycleException(sm.getString("coyoteConnector.protocolHandlerNoAprListener",getProtocolHandlerClassName()));}if (protocolHandler.isAprRequired() && !AprLifecycleListener.isAprAvailable()) {throw new LifecycleException(sm.getString("coyoteConnector.protocolHandlerNoAprLibrary",getProtocolHandlerClassName()));}if (AprLifecycleListener.isAprAvailable() && AprLifecycleListener.getUseOpenSSL() &&protocolHandler instanceof AbstractHttp11JsseProtocol) {AbstractHttp11JsseProtocol jsseProtocolHandler =(AbstractHttp11JsseProtocol) protocolHandler;if (jsseProtocolHandler.isSSLEnabled() &&jsseProtocolHandler.getSslImplementationName() == null) {jsseProtocolHandler.setSslImplementationName(OpenSSLImplementation.class.getName());}}try {protocolHandler.init();} catch (Exception e) {throw new LifecycleException(sm.getString("coyoteConnector.protocolHandlerInitializationFailed"), e);} } 2.5Catalina#start() public void start(){if (getServer() == null) {load();}if (getServer() == null) {log.fatal("Cannot start server. Server instance is not configured.");return;}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;}if (useShutdownHook) {if (shutdownHook == null) {shutdownHook = new CatalinaShutdownHook();}Runtime.getRuntime().addShutdownHook(shutdownHook);LogManager logManager = LogManager.getLogManager();if (logManager instanceof ClassLoaderLogManager) {((ClassLoaderLogManager) logManager).setUseShutdownHook(false);}}if (await) {await();stop();} } 上面主要是启动tomcat中各个组件、容器,这时候还需要创建等待关闭tomcat的ServerSocket以及它监听的端口,而await()方法的作用正是创建ServerSocket并等待接收关闭命令的。 @Override public void await(){//该处省略部分代码...//创建ServerSocker并监听端口try {awaitSocket = new ServerSocket(port, 1,InetAddress.getByName(address));} catch (IOException e) {log.error("StandardServer.await: create[" + address+ ":" + port+ "]: ", e);return;}try {awaitThread = Thread.currentThread();// 循环等待有效连接和命令while (!stopAwait) {ServerSocket serverSocket = awaitSocket;if (serverSocket == null) {break;}// 等待下一个连接Socket socket = null;StringBuilder command = new StringBuilder();try {InputStream stream;long acceptStartTime = System.currentTimeMillis();try {socket = serverSocket.accept();socket.setSoTimeout(10 * 1000); // Ten secondsstream = socket.getInputStream();} catch (SocketTimeoutException ste) {log.warn(sm.getString("standardServer.accept.timeout",Long.valueOf(System.currentTimeMillis() - acceptStartTime)), ste);continue;} catch (AccessControlException ace) {log.warn(sm.getString("standardServer.accept.security"), ace);continue;} catch (IOException e) {if (stopAwait) {break;}log.error(sm.getString("standardServer.accept.error"), e);break;}// 从套接字读取一组字符int expected = 1024; // Cut off to avoid DoS attackwhile (expected < shutdown.length()) {if (random == null)random = new Random();expected += (random.nextInt() % 1024);}while (expected > 0) {int ch = -1;try {ch = www.sh-musen.com();} catch (IOException e) {log.warn(sm.getString("standardServer.accept.readError"), e);ch = -1;}// 若字符是控制字符或者EOF(-1)则终止循环if (ch < 32 || ch == 127) {break;}command.append((char) ch);expected--;}} finally {// 完成操作后关闭sockettry {if (socket != null) {socket.close();}} catch (IOException e) {// Ignore}}// 判断命令内容是否SHUTDOWNboolean match = command.toString().equals(shutdown);if (match) {www.sh-musen.com(sm.getString("standardServer.shutdownViaPort"));break;} elselog.warn(sm.getString("standardServer.invalidShutdownCommand", command.toString()));}} finally {ServerSocket serverSocket = awaitSocket;awaitThread = null;awaitSocket = null;// 关闭ServerScoketif (serverSocket != null) {try {serverSocket.close();} catch (IOException e) {}}} } 结束