Tomcat源码阅读#1:classloader初始化
来源:互联网 发布:app个性化推荐算法 编辑:程序博客网 时间:2024/05/17 23:26
Bootstrap
通过Tomcat的启动脚本可以看到启动的入口是在Bootstrap
,来看下Bootstrap
的main
方法,
/** * 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) { //////////////////////////////////////// // 1. Bootstrap初始化 //////////////////////////////////////// // 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); //////////////////////////////////////// // 2. org.apache.catalina.startup.Catalina#load //////////////////////////////////////// daemon.load(args); //////////////////////////////////////// // 3. org.apache.catalina.startup.Catalina#start //////////////////////////////////////// daemon.start(); } 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); } }
来看下init
方法,
/** * Initialize daemon. */ public void init() throws Exception { //////////////////////////////////////// // 1. 设置catalina.home与catalina.base //////////////////////////////////////// // Set Catalina path setCatalinaHome(); setCatalinaBase(); //////////////////////////////////////// // 2. classloader初始化 //////////////////////////////////////// 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"); //////////////////////////////////////// // 3. 使用catalinaLoader加载Catalina //////////////////////////////////////// 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"); //////////////////////////////////////// // 4. 将Catalina的parentClassLoader设置为sharedLoader //////////////////////////////////////// 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; }
可以看到需要先初始化ClassLoader,然后才去加载最重要的服务器类,也就是org.apache.catalina.startup.Catalina
。有三个ClassLoader,
protected ClassLoader commonLoader = null; protected ClassLoader catalinaLoader = null; protected ClassLoader sharedLoader = null;
下面来看下ClassLoader初始化的代码,
private void initClassLoaders() { try { //////////////////////////////////////// // commonLoader的parent置为null //////////////////////////////////////// commonLoader = createClassLoader("common", null); //////////////////////////////////////// // 如果没有配置common.loader,则commonLoader就是AppClassLoader //////////////////////////////////////// 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 { //////////////////////////////////////// // 从catalina.base/conf/catalina.properties读取配置 //////////////////////////////////////// String value = CatalinaProperties.getProperty(name + ".loader"); //////////////////////////////////////// // 默认情况下catalinaLoader与sharedLoader配置为空 // 所以三个ClassLoader其实是同一个引用 //////////////////////////////////////// if ((value == null) || (value.equals(""))) return parent; value = replace(value); //////////////////////////////////////// // 添加配置的classpath //////////////////////////////////////// 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)); } } //////////////////////////////////////// // 差不多是new URLClassLoader //////////////////////////////////////// ClassLoader classLoader = ClassLoaderFactory.createClassLoader (repositories, parent); // Retrieving MBean server MBeanServer mBeanServer = null; if (MBeanServerFactory.findMBeanServer(null).size() > 0) { mBeanServer = MBeanServerFactory.findMBeanServer(null).get(0); } else { mBeanServer = ManagementFactory.getPlatformMBeanServer(); } // Register the server classloader ObjectName objectName = new ObjectName("Catalina:type=ServerClassLoader,name=" + name); mBeanServer.registerMBean(classLoader, objectName); return classLoader; }
ClassLoader的配置放在catalina.properties
,
### List of comma-separated paths defining the contents of the "common"# classloader. Prefixes should be used to define what is the repository type.# Path may be relative to the CATALINA_HOME or CATALINA_BASE path or absolute.# If left as blank,the JVM system loader will be used as Catalina's "common"# loader.# Examples:# "foo": Add this folder as a class repository# "foo/*.jar": Add all the JARs of the specified folder as class# repositories# "foo/bar.jar": Add bar.jar as a class repositorycommon.loader=${catalina.base}/lib,${catalina.base}/lib/*.jar,${catalina.home}/lib,${catalina.home}/lib/*.jar## List of comma-separated paths defining the contents of the "server"# classloader. Prefixes should be used to define what is the repository type.# Path may be relative to the CATALINA_HOME or CATALINA_BASE path or absolute.# If left as blank, the "common" loader will be used as Catalina's "server"# loader.# Examples:# "foo": Add this folder as a class repository# "foo/*.jar": Add all the JARs of the specified folder as class# repositories# "foo/bar.jar": Add bar.jar as a class repositoryserver.loader=## List of comma-separated paths defining the contents of the "shared"# classloader. Prefixes should be used to define what is the repository type.# Path may be relative to the CATALINA_BASE path or absolute. If left as blank,# the "common" loader will be used as Catalina's "shared" loader.# Examples:# "foo": Add this folder as a class repository# "foo/*.jar": Add all the JARs of the specified folder as class# repositories# "foo/bar.jar": Add bar.jar as a class repository# Please note that for single jars, e.g. bar.jar, you need the URL form# starting with file:.shared.loader=
可以看到只有commonLoader
才有配置,所以createClassLoader("server", commonLoader);
与createClassLoader("shared", commonLoader);
返回的都会是commonLoader
。
还有一点值得注意的是,commonLoader
的parent
是null
,也就是说当commonLoader
进行类加载要委托给父类加载器时,将会绕过AppClassLoader
和ExtClassLoader
,直接交给BootstrapClassLoader
(不明白的同学可以参考这里)。
Catalina
使用catalinaLoader
加载了Catalina
之后,会调用Catalina#load
方法,
public void load() { long t1 = System.nanoTime(); //////////////////////////////////////// // 设置catalina.home与catalina.base //////////////////////////////////////// initDirs(); // Before digester - it may be needed initNaming(); //////////////////////////////////////// // 创建server.xml的解析器 //////////////////////////////////////// // Create and execute our Digester Digester digester = createStartDigester(); //////////////////////////////////////// // 读取catalina.base/conf/server.xml //////////////////////////////////////// InputSource inputSource = null; InputStream inputStream = null; File file = null; 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; } //////////////////////////////////////// // 解析server.xml //////////////////////////////////////// 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 { try { inputStream.close(); } catch (IOException e) { // Ignore } } getServer().setCatalina(this); // Stream redirection initStreams(); //////////////////////////////////////// // org.apache.catalina.Server初始化 //////////////////////////////////////// // Start the new server 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); } } long t2 = System.nanoTime(); if(log.isInfoEnabled()) { log.info("Initialization processed in " + ((t2 - t1) / 1000000) + " ms"); } }
load
方法做的事情就是通过server.xml
的配置去初始化Server
。随后的Catalina#start
其实就是去启动Server
,
public void start() { if (getServer() == null) { load(); } if (getServer() == null) { log.fatal("Cannot start server. Server instance is not configured."); return; } long t1 = System.nanoTime(); //////////////////////////////////////// // 启动org.apache.catalina.Server //////////////////////////////////////// // Start the new server 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(); } }
alright,今天就先到这吧。
参考资料
- https://tomcat.apache.org/tomcat-7.0-doc/building.html
- https://tomcat.apache.org/tomcat-7.0-doc/class-loader-howto.html
- https://tomcat.apache.org/tomcat-7.0-doc/architecture/overview.html
- https://tomcat.apache.org/tomcat-7.0-doc/architecture/startup.html
- https://tomcat.apache.org/tomcat-7.0-doc/architecture/requestProcess.html
0 0
- Tomcat源码阅读#1:classloader初始化
- 1、Tomcat源码阅读之初始化流程梳理
- tomcat源码阅读(三)——ClassLoader背景知识
- Tomcat源码阅读之Bootstrap启动流程与classLoader设计
- Tomcat源码阅读(一)初始化
- Tomcat源码阅读之初始化Server组件
- Tomcat源码阅读之初始化连接器组件
- tomcat源码阅读-1
- Tomcat源码阅读1
- Tomcat源码学习-1-初始化
- tomcat源码阅读(二)——ClassLoader及catalina启动
- 通过Tomcat源码学习ClassLoader
- tomcat源码阅读的趣事1
- tomcat源码阅读-2
- tomcat源码阅读-3.1
- Tomcat源码阅读计划
- Tomcat源码阅读
- Tomcat源码阅读系列
- java 登陆和注册传输的加密解密
- linux下C++开发常用工具
- hibernate many to one 非主键关联
- 定制化创建角点检测子--cornerEigenValsAndVecs()和cornerMinEigenVal()
- 第三方库SlidingMenu的使用
- Tomcat源码阅读#1:classloader初始化
- 用L-BFGS法训练最大熵模型做分类
- jquery预加载的几种方式
- 【ios开发学习 - 第三课】UITextField使用
- ubuntu下python+flask+mysql完整开发环境配置
- 点击单选按钮改变div显示隐藏
- C语言指针实现字符串的反转
- leetcode--Gray Code
- JQuery操作option的添加、删除、取值