Tomcat源码-起动.初始化(加载类包)分析三
一,启动
Tomcat是从org.apache.catalina.startup.Bootstrap#main()开始启动.代码如下:
public static void main(String args[]) { if (daemon == null) { daemon = new Bootstrap(); try { daemon.init(); } catch (Throwable t) { t.printStackTrace(); return; } } try { String command = "start"; if (args.length > 0) { command = args[args.length - 1]; } if (command.equals("startd")) { args[0] = "start"; daemon.load(args); daemon.start(); } else if (command.equals("stopd")) { args[0] = "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 { log.warn("Bootstrap: command \"" + command + "\" does not exist."); } } catch (Throwable t) { t.printStackTrace(); } }
从以上可以很清楚的看懂tomcat是通过参数的不同进行相应的命令调用.
启动之前要进行相应的init()初始化,进行相应的环境设置以及包的加,以下我们查看下init的方法.
二,初始化(Bootstrap#init())
public void init()throws Exception { // Set Catalina path setCatalinaHome(); setCatalinaBase(); //将tomcat/lib下的jar包进行加载 initClassLoaders(); //将classload设置进线程,以便我们使用时进行调用 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(); // 将sharedLoader设置进Catalina类中 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; }
从以上的初始化我们对他一一进行分析
1,
setCatalinaHome(); setCatalinaBase();
根据源代码主要设置了以下路径:
System.setProperty("catalina.home", System.getProperty("user.dir"));
2,
//将tomcat/lib下的jar包进行加载 initClassLoaders();
tomcat中的加载方式是:
a) Set up classloaders
commonLoader (common)-> System Loader
sharedLoader (shared)-> commonLoader -> System Loader
catalinaLoader(server) -> commonLoader -> System Loader
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) { log.error("Class loader creation threw exception", t); System.exit(1); } }
createClassLoader的加载方式:
//这个类的主要作用是从 catalina.properties读取信息进行加载
private ClassLoader createClassLoader(String name, ClassLoader parent) throws Exception { // 获取与ClassLoader相关的属性值,可能为 common.loader,server.loader, shared.loader // 定义在org/apache/catalina/startup/catalina.properties String value = CatalinaProperties.getProperty(name + ".loader"); if ((value == null) || (value.equals(""))) return parent; ArrayList repositoryLocations = new ArrayList(); ArrayList repositoryTypes = new ArrayList(); int i; StringTokenizer tokenizer = new StringTokenizer(value, ","); while (tokenizer.hasMoreElements()) { String repository = tokenizer.nextToken(); // Local repository boolean replace = false; String before = repository; while ((i=repository.indexOf(CATALINA_HOME_TOKEN))>=0) { replace=true; if (i>0) { repository = repository.substring(0,i) + getCatalinaHome() + repository.substring(i+CATALINA_HOME_TOKEN.length()); } else { repository = getCatalinaHome() + repository.substring(CATALINA_HOME_TOKEN.length()); } } while ((i=repository.indexOf(CATALINA_BASE_TOKEN))>=0) { replace=true; if (i>0) { repository = repository.substring(0,i) + getCatalinaBase() + repository.substring(i+CATALINA_BASE_TOKEN.length()); } else { repository = getCatalinaBase() + repository.substring(CATALINA_BASE_TOKEN.length()); } } if (replace && log.isDebugEnabled()) log.debug("Expanded " + before + " to " + replace); // Check for a JAR URL repository try { URL url=new URL(repository); repositoryLocations.add(repository); repositoryTypes.add(ClassLoaderFactory.IS_URL); continue; } catch (MalformedURLException e) { // Ignore } if (repository.endsWith("*.jar")) { repository = repository.substring (0, repository.length() - "*.jar".length()); repositoryLocations.add(repository); repositoryTypes.add(ClassLoaderFactory.IS_GLOB); } else if (repository.endsWith(".jar")) { repositoryLocations.add(repository); repositoryTypes.add(ClassLoaderFactory.IS_JAR); } else { repositoryLocations.add(repository); repositoryTypes.add(ClassLoaderFactory.IS_DIR); } } String[] locations = (String[]) repositoryLocations.toArray(new String[0]); Integer[] types = (Integer[]) repositoryTypes.toArray(new Integer[0]); ClassLoader classLoader = ClassLoaderFactory.createClassLoader (locations, types, parent); // Retrieving MBean server MBeanServer mBeanServer = null; if (MBeanServerFactory.findMBeanServer(null).size() > 0) { mBeanServer = (MBeanServer) MBeanServerFactory.findMBeanServer(null).get(0); } else { mBeanServer = MBeanServerFactory.createMBeanServer(); } ///用jmx对该classloader进行管理 // Register the server classloader ObjectName objectName = new ObjectName("Catalina:type=ServerClassLoader,name=" + name); mBeanServer.registerMBean(classLoader, objectName); return classLoader; }
根据以上的设置tomcat主要对类加载作了以下方式:
b) Load startup class (reflection)
org.apache.catalina.startup.Catalina
setParentClassloader -> sharedLoader
Thread.contextClassloader -> catalinaLoader
---------------------------------------------------
以下对java中的类加载进行归纳:
可参考文章:http://blog.chenlb.com/2009/06/java-classloader-architecture.html
以上用线程设置classloader的目的是:
java默认的线程上下文类加载器是 系统类加载器(AppClassLoader)。
使用Thread.currentThread().setContextClassLoader(classloader)时,线程上下文 Classloader就变成了指定的Classloader了。此时,在本线程或子线程的任意一处地方,调用Thread.currentThread(). getContextClassLoader(),都可以得到前面设置的Classloader。
如果要扩展classloader对类包进行加载也可扩展URLCloassLoader类