副标题#e#
在上一章查察tomcat启动文件都干点啥—catalina.bat,说了在catalina.bat中都走了什么流程,最重要的是,我们得出了如下这段呼吁:
_EXECJAVA=start "Tomcat" "E:\Program Files\Java\jdk1.7.0_40\bin\java"
JAVA_OPTS= -Djava.util.logging.config.file="F:\apache-tomcat-7.0.8\conf\logging.properties"
-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager
CATALINA_OPTS=
DEBUG_OPTS=
-Djava.endorsed.dirs="F:\apache-tomcat-7.0.8\endorsed"
-classpath "F:\apache-tomcat-7.0.8\bin\bootstrap.jar;F:\apache-tomcat-7.0.8\bin\tomcat-juli.jar"
-Dcatalina.base="F:\apache-tomcat-7.0.8"
-Dcatalina.home="F:\apache-tomcat-7.0.8"
-Djava.io.tmpdir="F:\apache-tomcat-7.0.8\temp"
MAINCLASS=org.apache.catalina.startup.Bootstrap
CMD_LINE_ARGS= ACTION=start
个中很重要的一个属性是:MAINCLASS=org.apache.catalina.startup.Bootstrap,Bootstrap在bootstrap.jar中,我们看一下Bootstrap的类图:
从每个变量和要领的名字的字面上也能或许看出来变量可能要领的浸染。
很显然,措施走到Bootstrap的时候首先挪用main要领,main要领是通过剧本启动tomcat的进口,我们看一下main要领中实现的内容:
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(); } 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); }
可以看出main要领主要实现了两个成果:
(1)初始化一个守护历程变量。
(2)加载参数,理会呼吁,并执行。
下面是初始化守护历程的执行进程:
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); }
可以看到在bootstrap.init()要领中对bootstrap变量举办初始化,然后将功效返回给daemon。下面看一下init要领中的实现:
public void init() throws Exception { // Set Catalina path setCatalinaHome(); setCatalinaBase(); 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.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; }
init要领中对classLoader举办了初始化,配置了引用的catalinaDaemon变量。
#p#副标题#e#
#p#分页标题#e#
对付如何界说catalinaDaemon变量是应用反射机制:
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;
下面着重说一下关于classLoader。
在Bootstrap类中,最开始的处所,有三个ClassLoader的界说,内容如下:
protected ClassLoader commonLoader = null;
protected ClassLoader catalinaLoader = null;
protected ClassLoader sharedLoader = null;
先不说这三个classLoader之间的干系,先看一下Bootstrap上的注释也有关于classLoader的说明:
* Bootstrap loader for Catalina. This application constructs a class loader * for use in loading the Catalina internal classes (by accumulating all of the * JAR files found in the "server" directory under "catalina.home"), and * starts the regular execution of the container. The purpose of this * roundabout approach is to keep the Catalina internal classes (and any * other classes they depend on, such as an XML parser) out of the system * class path and therefore not visible to application level classes.
翻译过来就是说:Bootstrap第一个成果是引导Catalina,Bootstrap结构一个class loader来加载Catalina的内部类(所有在catalina.home中的jar文件),第二个成果是启动container。实现catalina的内部类和系统的class path以及应用措施中的class要区分隔不能彼此会见的目标。
// Set Catalina path
setCatalinaHome();
setCatalinaBase();
initClassLoaders();
Thread.currentThread().setContextClassLoader(catalinaLoader);
SecurityClassLoad.securityClassLoad(catalinaLoader);
配置当前的线程的class loader为catalinaLoader。利用catalinaLoader加载catalina类,实现反射,界说catalina的反射。然后重点看一下 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); }
在看一下createClassLoader要领的内容:
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<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)); } } 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; }
在该要领中引入了CatalinaProperties类,下面看一下CatalinaProperties的内容:
#p#分页标题#e#
这个类主要是加载catalina.properties设置文件,然后将个中的内容生存到当前情况中,首先查察$CATALINA_BASE/conf/catalina.propertie是否存在,假如不存在的话去读取Bootstrap.jar中的catalina.propertie的文件,假如没有在$CATALINA_BASE/conf/中设置catalina.propertie文件,那么catalina.propertie内容如下所示(tomcat版本大于5.x):
# Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # List of comma-separated packages that start with or equal this string # will cause a security exception to be thrown when # passed to checkPackageAccess unless the # corresponding RuntimePermission ("accessClassInPackage."+package) has # been granted. package.access=sun.,org.apache.catalina.,org.apache.coyote.,org.apache.tomcat.,org.apache.jasper. # # List of comma-separated packages that start with or equal this string # will cause a security exception to be thrown when # passed to checkPackageDefinition unless the # corresponding RuntimePermission ("defineClassInPackage."+package) has # been granted. # # by default, no packages are restricted for definition, and none of # the class loaders supplied with the JDK call checkPackageDefinition. # package.definition=sun.,java.,org.apache.catalina.,org.apache.coyote.,org.apache.tomcat.,org.apache.jasper. # # # 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 repository common.loader=${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 repository server.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= # # String cache configuration. tomcat.util.buf.StringCache.byte.enabled=true #tomcat.util.buf.StringCache.char.enabled=true #tomcat.util.buf.StringCache.trainThreshold=500000 #tomcat.util.buf.StringCache.cacheSize=5000
在接洽在Bootstrap.java中的这段代码:
String value = CatalinaProperties.getProperty(name + ".loader");
if ((value == null) || (value.equals("")))
return parent;
#p#分页标题#e#
有没有感受很奇怪,shared.loader,server.loader这两个设置都为空,所以通过initClassLoaders要领可以得出结论,catalinaLoader,sharedLoader均指向commonLoader的引用,所以在apache网站上的classLoader树如下展示:
可是拿两个classLoader为什么会界说呢,那就要看一下在tomcat5.x时代时候的catalina.propertie内容:
server.loader=${catalina.home}/server/classes,${catalina.home}/server/lib/*.jar
shared.loader=${catalina.base}/shared/classes,${catalina.base}/shared/lib/*.jar
catalinaLoader,sharedLoader这大概就是汗青遗留的问题了。关于这两个classLoader的内容请在tomcat5.x找谜底。
OK,下面我们只需要存眷commonLoader变量了,在catalina.properties设置文件中可以得出
common.loader=${catalina.home}/lib,${catalina.home}/lib/*.jar
下面相信看一下tomcat如那里理惩罚commonLoader:
当createClassLoader的参数为common,null的时候,
String value = CatalinaProperties.getProperty(name + ".loader");
此时的value为设置文件中的值(在我的调试情况中,下同):
${catalina.home}/lib,${catalina.home}/lib/*.jar
查察本栏目
这个变量大概是不能理会的,需要将${catalina.home}替换为系统的据对路径,通过replace要领替换,可以看一下replace的界说:
protected String replace(String str) { // Implementation is copied from ClassLoaderLogManager.replace(), // but added special processing for catalina.home and catalina.base. String result = str; int pos_start = str.indexOf("${"); if (pos_start >= 0) { StringBuilder builder = new StringBuilder(); int pos_end = -1; while (pos_start >= 0) { builder.append(str, pos_end + 1, pos_start); pos_end = str.indexOf('}', pos_start + 2); if (pos_end < 0) { pos_end = pos_start - 1; break; } String propName = str.substring(pos_start + 2, pos_end); String replacement; if (propName.length() == 0) { replacement = null; } else if (Globals.CATALINA_HOME_PROP.equals(propName)) { replacement = getCatalinaHome(); } else if (Globals.CATALINA_BASE_PROP.equals(propName)) { replacement = getCatalinaBase(); } else { replacement = System.getProperty(propName); } if (replacement != null) { builder.append(replacement); } else { builder.append(str, pos_start, pos_end + 1); } pos_start = str.indexOf("${", pos_end + 1); } builder.append(str, pos_end + 1, str.length()); result = builder.toString(); } return result; }
通过replace返回的功效为:
value=F:\ITSM_V3.1\tomcatSource/lib,F:\ITSM_V3.1\tomcatSource/lib/*.jar
然后顺序执行,很容易得出结论,在执行
ClassLoader classLoader = ClassLoaderFactory.createClassLoader(repositories, parent);
时候的repositories的值为:
#p#分页标题#e#
挪用ClassLoaderFactory类中的静态要领createClassLoader,其界说如下:
public static ClassLoader createClassLoader(List<Repository> repositories, final ClassLoader parent) throws Exception { if (log.isDebugEnabled()) log.debug("Creating new class loader"); // Construct the "class path" for this class loader Set<URL> set = new LinkedHashSet<URL>(); if (repositories != null) { for (Repository repository : repositories) { if (repository.getType() == RepositoryType.URL) { URL url = new URL(repository.getLocation()); if (log.isDebugEnabled()) log.debug(" Including URL " + url); set.add(url); } else if (repository.getType() == RepositoryType.DIR) { File directory = new File(repository.getLocation()); directory = directory.getCanonicalFile(); if (!validateFile(directory, RepositoryType.DIR)) { continue; } URL url = directory.toURI().toURL(); if (log.isDebugEnabled()) log.debug(" Including directory " + url); set.add(url); } else if (repository.getType() == RepositoryType.JAR) { File file=new File(repository.getLocation()); file = file.getCanonicalFile(); if (!validateFile(file, RepositoryType.JAR)) { continue; } URL url = file.toURI().toURL(); if (log.isDebugEnabled()) log.debug(" Including jar file " + url); set.add(url); } else if (repository.getType() == RepositoryType.GLOB) { File directory=new File(repository.getLocation()); directory = directory.getCanonicalFile(); if (!validateFile(directory, RepositoryType.GLOB)) { continue; } if (log.isDebugEnabled()) log.debug(" Including directory glob " + directory.getAbsolutePath()); String filenames[] = directory.list(); for (int j = 0; j < filenames.length; j++) { String filename = filenames[j].toLowerCase(Locale.ENGLISH); if (!filename.endsWith(".jar")) continue; File file = new File(directory, filenames[j]); file = file.getCanonicalFile(); if (!validateFile(file, RepositoryType.JAR)) { continue; } if (log.isDebugEnabled()) log.debug(" Including glob jar file " + file.getAbsolutePath()); URL url = file.toURI().toURL(); set.add(url); } } } } // Construct the class loader itself final URL[] array = set.toArray(new URL[set.size()]); if (log.isDebugEnabled()) for (int i = 0; i < array.length; i++) { log.debug(" location " + i + " is " + array[i]); } return AccessController.doPrivileged( new PrivilegedAction<StandardClassLoader>() { @Override public StandardClassLoader run() { if (parent == null) return new StandardClassLoader(array); else return new StandardClassLoader(array, parent); } }); }
最后返回的 return new StandardClassLoader(array);个中array是一个URL范例的数组,当作果的时候要比较前面的内容file:/F:/ITSM_V3.1/tomcatSource/lib/, file:/F:/ITSM_V3.1/tomcatSource/lib/annotations-api.jar…….(file:/F:/ITSM_V3.1/tomcatSource/lib/下的所有jar文件)。
然后将classLoader注册到MBeanServer中,然后返回classLoader。然后用返回的classLoader加载org.apache.catalina.startup.Catalina,然后举办反射。挪用对应的要领。
下面说一下main要领的第二个浸染,加载参数,理会呼吁,并执行
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(); } 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."); }
#p#分页标题#e#
个中load要领是将呼吁行界说的参数通报给通过反射挪用的catalinaDaemon.load要领。然后执行的要领也就是在实现的时候挪用catalinaDaemon中对应的要领,譬喻:
/** * Stop the Catalina Daemon. */ public void stop() throws Exception { Method method = catalinaDaemon.getClass().getMethod("stop", (Class [] ) null); method.invoke(catalinaDaemon, (Object [] ) null); }
在此文中我们得出在(tomcat7.0版本中):
(1)Bootstrap中如何通过建设的commonLoader=catalinaLoader=sharedLoader来加载类。
(2)在Bootstrap中利用反射机警来加载来挪用catalinaDaemon中的要领。
(3)如何获取catalina.properties设置文件。