`
banxi1988
  • 浏览: 153934 次
  • 性别: Icon_minigender_1
  • 来自: 桂林
社区版块
存档分类
最新评论

Tomcat学习之二,认识Bootstrap类

阅读更多
Bootstrap类全称org.apache.catalina.startup.Bootstrap
整个类加上注释和空白也就559行.代码写得很规整.到底人家是世界级的代码嘛.或者java的代码格式很容易写清楚.
整个类中有22个方法,六个成员变量 ,还有一个日志成员变量 .可见平均下来类中的方法也就10多行代码这个样子.我喜欢类中方法分类得当的.太长方法说明设计不当或者实在是逻辑很复杂.
这个类注释如下:
/**
 * 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.

 * -------------------------我的翻译如下:-----------------------------------------
 * Catalina的Bootstrap加载器(loader).此程序构造了一个类加载器(class loader)用来在加载Catalina的内部类(internal classes)(即汇集在"catalina.home"下"server"目录中的所有JAR文件,和启动容器的常规执行(regular execution).这样绕着弯子的做法是为了保持Catalina的内部类(和它依赖的任何类,例如XMP解析器)不在系统中类路径(system class path)中,从而对应用程序的类就不可见了.
 */

我们来看下Bootstrap的main方法(不知道为什么,对于程序自从C开始,我就首先去找main函数,找不到我就有点不太爽.)主要就是初始为类加载器并启动此类.如下:
    /**
     * Main method, used for testing only.
     *
     * @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;
        }
       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) {
            handleThrowable(t);
            // Unwrap the Exception for clearer error reporting
            if (t instanceof InvocationTargetException &&
                    t.getCause() != null) {
                t = t.getCause();
            }
            t.printStackTrace();
            System.exit(1);
        }

    }


(#q1)上面那句注释中说的仅用于测试,难道正确的启动就不用了吗?
还有对于下面的代码:
          String command = "start";
            if (args.length > 0) {
                command = args[args.length - 1];
            }


(#q2) 为什么判断了args.length之后,是用args[args.length - 1]而不是用args[0]呢?

从此代码中,我还了解到了一点,tomcat7源代码中的try{}catch(Throwable t){}
catch子句中,catch的都是Throwable.之前我一直觉得Exception是根本,思考了之下.
我看了下jdk.原来Exception还有一个Throwable超类.同时Throwable也是Errors的超类.

引用

Throwable 类是 Java 语言中所有错误或异常的超类,只有当对象是此类(或其子类之一)的实例时,才能通过 Java 虚拟机或者 Java throw 语句抛出。类似地,只有此类或其子类之一才可以是 catch 子句中的参数类型。


我们看下main中调用的初始化方法即bootstrap.init().
看下初始化方法都做了哪些事件先:
    /**
     * Initialize daemon.
     */
    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;

    }

上面的方法也比较清晰易懂:
先是设置Catalina的路径.
(吐槽下啊.为什么叫catalina这个名字呢?我google了下,没有找到什么好的解释:
不过google给出的第一个网站的说明是:
If you're planning a trip to Catalina Island, this should be your first stop. The Catalina Island Guide includes packages, activities, lodging, transportation, local ...

是不是因为tomcat的开发者喜欢这个Catalina Island啊?
)
设置路径之后,调用初始化类加载器的方法即initClassLoaders()
如下:
 
private void initClassLoader(){
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);
}
}


初始化类加载器,主要也是三个步骤.即创建一个公共类加载器commonLoader.
然后以commonLoader为父类加载器.创标签名为"server"的catalinaLoader类加载器,和标签名为"shared"的sharedLoader类加载器.
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{
         @SupperssWarning("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 Reposiotry(repository,RepositoryType.GLOB);
     }else if(repository.endsWith(".jar")){
       repositories.add(new Reposiotry(repository,RepositoryType.JAR);
     }else {
       repositories.add(new Reposiotry(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;

}

上面的方法中第一句代码:
 String value = CatalinaProperties.getProperty(name+".loader");

CatalinaProperties就是指tomcat/conf目录下的catalina.properties文件的配置.
common.loader的value值为:
引用

common.loader=${catalina.base}/lib,${catalina.base}/lib/*.jar,${catalina.home}/lib,${catalina.home}/lib/*.jar


 value = replace(value)

上面这一句代码执行过后,value值,在我的系统中变成了如下:
引用

"/home/banxi1988/work/tomcat7/tomcat-7.0.x/output/build/lib,/home/banxi1988/work/tomcat7/tomcat-7.0.x/output/build/lib/*.jar,/home/banxi1988/work/tomcat7/tomcat-7.0.x/output/build/lib,/home/banxi1988/work/tomcat7/tomcat-7.0.x/output/build/lib/*.jar"

上面出现的Repository是ClassLoaderFactory的静态内部类,
RepositoryType是ClassLoaderFactory的静态内部枚举.
代码如下:
    public static enum RepositoryType {
        DIR,
        GLOB,
        JAR,
        URL
    }
    
    public static class Repository {
        private String location;
        private RepositoryType type;
        
        public Repository(String location, RepositoryType type) {
            this.location = location;
            this.type = type;
        }
        
        public String getLocation() {
            return location;
        }
        
        public RepositoryType getType() {
            return type;
        }
    }

在确定的类资源的仓库之后,接下来就到了tomcat加载类最主要的部分了.
ClassLoader classLoader = ClassLoaderFactory.createClassLoader(repositories,parent);

在ClassLoaderFactory的createClassLoader方法中,先是将repositories中路径,变成一个
包含仓库中jar包文件的URL路径的数组:
然后以此数组为参数返回一个标准的类加载器(StandardClassLoader).
此方法中的关键代码如下:
        // 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);
                    }
                });

接我上面环境.此代码执行过程,array数组中的内容如下所示:

引用

[file:/home/banxi1988/work/tomcat7/tomcat-7.0.x/output/build/lib/, file:/home/banxi1988/work/tomcat7/tomcat-7.0.x/output/build/lib/jasper-el.jar, file:/home/banxi1988/work/tomcat7/tomcat-7.0.x/output/build/lib/tomcat-jdbc.jar, file:/home/banxi1988/work/tomcat7/tomcat-7.0.x/output/build/lib/tomcat-api.jar, file:/home/banxi1988/work/tomcat7/tomcat-7.0.x/output/build/lib/tomcat-i18n-ja.jar, file:/home/banxi1988/work/tomcat7/tomcat-7.0.x/output/build/lib/catalina-ha.jar, file:/home/banxi1988/work/tomcat7/tomcat-7.0.x/output/build/lib/tomcat-dbcp.jar, file:/home/banxi1988/work/tomcat7/tomcat-7.0.x/output/build/lib/ecj-3.7.jar, file:/home/banxi1988/work/tomcat7/tomcat-7.0.x/output/build/lib/tomcat-coyote.jar, file:/home/banxi1988/work/tomcat7/tomcat-7.0.x/output/build/lib/tomcat-i18n-fr.jar, file:/home/banxi1988/work/tomcat7/tomcat-7.0.x/output/build/lib/jsp-api.jar, file:/home/banxi1988/work/tomcat7/tomcat-7.0.x/output/build/lib/tomcat-i18n-es.jar, file:/home/banxi1988/work/tomcat7/tomcat-7.0.x/output/build/lib/el-api.jar, file:/home/banxi1988/work/tomcat7/tomcat-7.0.x/output/build/lib/jasper.jar, file:/home/banxi1988/work/tomcat7/tomcat-7.0.x/output/build/lib/catalina.jar, file:/home/banxi1988/work/tomcat7/tomcat-7.0.x/output/build/lib/catalina-tribes.jar, file:/home/banxi1988/work/tomcat7/tomcat-7.0.x/output/build/lib/servlet-api.jar, file:/home/banxi1988/work/tomcat7/tomcat-7.0.x/output/build/lib/tomcat-util.jar, file:/home/banxi1988/work/tomcat7/tomcat-7.0.x/output/build/lib/annotations-api.jar, file:/home/banxi1988/work/tomcat7/tomcat-7.0.x/output/build/lib/catalina-ant.jar]


现在我们来仔细看下上面那个复杂的返回语句:
首先了解下AccessController类.它在java.security包下:
jdk_api_1.6对此类的使用目的说明如下:
引用

AccessController 类用于与访问控制相关的操作和决定。

更确切地说,AccessController 类用于以下三个目的:

    基于当前生效的安全策略决定是允许还是拒绝对关键系统资源的访问

    将代码标记为享有“特权”,从而影响后续访问决定,以及

    获取当前调用上下文的“快照”,这样便可以相对于已保存的上下文作出其他上下文的访问控制决定。

如类名所示,AccessController是用来做访问权限控制的.
引用

可以将调用方标记为享有“特权”(请参阅 doPrivileged 及下文)。在做访问控制决定时,如果遇到通过调用不带上下文参数(请参阅下文,以获取关于上下文参数的信息)的 doPrivileged 标记为“特权”的调用方,则 checkPermission 方法将停止检查。如果该调用方的域具有指定的权限,则不进行进一步检查,并且 checkPermission 正常返回,指示允许所请求的访问。如果该域不具有指定的权限,则通常抛出异常。

“特权”功能的标准用法如下所示。如果不需要从“特权”块返回值,则使用以下代码:

   somemethod() {
        ...normal code here...
        AccessController.doPrivileged(new PrivilegedAction() {
            public Object run() {
                // privileged code goes here, for example:
                System.loadLibrary("awt");
                return null; // nothing to return
            }
        });
       ...normal code here...
  }

关于AccessController的具体详细使用的话,在此我也暂时无法分析清楚.
下面再分析下返回的标签类加载器.
return new StandardClassLoader(array);
这个类是在org.apache.catalina.loader包下.
整个类如下:
public class StandardClassLoader extends URLClassLoader implements StandardClassLoaderMBean{
  public StandardClassLoader(URL repositories[]){
   super(repositories);
  }
  public StandardClassLoader(URL repositories[],ClassLoader parent){
   super(repositories,parent);
  }

}

上面的类中,继承的类URLClassLoader在java.net包中.
而StandardClassLoaderMBean则是一个标记类如下:
package org.apache.catalina.loader;

/**
 * MBean interface for StandardClassLoader, to allow JMX remote management.
 *
 * @author Remy Maucherat
 * @version $Id: StandardClassLoaderMBean.java 988225 2010-08-23 17:38:41Z markt $
 */
public interface StandardClassLoaderMBean {
    // Marker interface
}

下面我们来重点了解下URLClassLoader类.
下面是JDK_API_1.6的文档说明:
引用

public class URLClassLoader
extends SecureClassLoader

该类加载器用于从指向 JAR 文件和目录的 URL 的搜索路径加载类和资源。这里假定任何以 '/' 结束的 URL 都是指向目录的。如果不是以该字符结束,则认为该 URL 指向一个将根据需要打开的 JAR 文件。

创建 URLClassLoader 实例的 AccessControlContext 线程将在后续加载类和资源时使用。

为加载的类默认授予只能访问 URLClassLoader 创建时指定的 URL 的权限。


下面是关于参数为URL[] urls的构造器的文档说明:
引用

URLClassLoader

public URLClassLoader(URL[] urls)

    使用默认的委托父 ClassLoader 为指定的 URL 构造一个新 URLClassLoader。首先在父类加载器中搜索 URL,然后按照为类和资源指定的顺序搜索 URL。这里假定任何以 '/' 结束的 URL 都是指向目录的。如果不是以该字符结束,则认为该 URL 指向一个将根据需要下载和打开的 JAR 文件。

    如果有安全管理器,该方法首先调用安全管理器的 checkCreateClassLoader 方法以确保允许创建类加载器。

    参数:
        urls - 从其位置加载类和资源的 URL
    抛出:
        SecurityException - 如果安全管理器存在并且其 checkCreateClassLoader 方法不允许创建类加载器。
    另请参见:
        SecurityManager.checkCreateClassLoader()


查看源代码如下:
    public URLClassLoader(URL[] urls) {
	super();
	// this is to make the stack depth consistent with 1.1
	SecurityManager security = System.getSecurityManager();
	if (security != null) {
	    security.checkCreateClassLoader();
	}
	ucp = new URLClassPath(urls);
	acc = AccessController.getContext();
    }

关于这个类加载器的创建过程就暂时分析到这里,下一往篇博文中将介绍下面提到的东西 .
上面main方法中关于command的if else 一堆.我们先来关注,正常情况下的启动过程:
else if (command.equals("start")) {
                daemon.setAwait(true);
                daemon.load(args);
                daemon.start();
            }




分享到:
评论

相关推荐

    天成美景后台管理系统(springboot+bootstrap)-CSDN.rar

    《天成美景后台管理系统:基于SpringBoot与Bootstrap的实践详解》 在当今信息化社会,后台管理系统作为企业运营的重要...此项目不仅是学习SpringBoot和Bootstrap实践的优秀案例,也是提升企业内部管理效率的有效工具。

    SSM+Bootstrap项目课程的源码qihangkt.zip

    通过学习和实践这个SSM+Bootstrap项目,开发者可以深入理解Java Web开发流程,提升前后端结合的能力,并对现代Web应用的架构有更全面的认识。同时,这个项目也适合那些想要学习和提升SSM框架和前端Bootstrap技能的...

    学习Java语言的30个参考,让你坐拥别人之上的30个擦考

    - **类加载器体系结构**:认识Bootstrap ClassLoader、Extension ClassLoader、Application ClassLoader的区别和作用。 - **类的生命周期管理**:掌握类的卸载条件和时机。 ### 4. GUI开发 - **Applet基础知识**:...

    java+web开发+复习资料+面试题

    Java Web 开发是软件行业中一个重要的领域,涵盖了服务器端编程、数据库...通过这些知识点的学习和实践,你可以对Java Web开发有更全面的认识,提升自己的技能水平,顺利通过面试。记得理论结合实践,不断巩固和提升。

    中国红在线新闻网.zip

    Tomcat 8.5是Apache Tomcat服务器的最新稳定版本,作为轻量级应用服务器,能很好地运行Java Servlet和JavaServer Pages。 通过解压"中国红在线新闻网.zip",我们可以得到整个项目的结构和内容,包括Java源代码、...

    百度地图毕业设计源码-Tmall_SSH:技术栈Struts2+Hibernate+Spring+Jsp+Tomcat,是JavaWeb入门非

    了,但如果接手老项目的话还是要懂的,学习过程我们也可以认识到它们当时优秀的设计理念, 当时解决了哪些痛点,后面又是因为什么被新技术替代,这样才能加深对 Java Web 整个平台的理解,不亏。 项目用到的技术如下...

    计算机毕业实习报告.pdf

    在实习中,实习生学习了如何利用反射机制来操作类和对象,增强了对Java动态性理解。 3. **Servlet**:Servlet是Java Web开发中处理HTTP请求的核心组件。实习期间,实习生学习了Servlet的工作原理,包括它们如何响应...

    Java Web项目之物流配货项目源码.zip

    学习这个项目,开发者可以深入理解Java Web开发流程,熟悉Spring框架的使用,掌握数据库操作和前端技术,同时对物流行业的业务逻辑有更直观的认识。这将有助于提升开发者在实际项目中的应用能力。

    java在线考试系统源代码

    源代码的提供使得开发者能够深入理解系统的架构和逻辑,从而进行二次开发或者学习。 在Java Web开发中,该系统可能采用了MVC(Model-View-Controller)设计模式,将业务逻辑、数据模型和用户界面分离,提高代码的可...

    Java项目实战-城市公交查询系统(附源码,部署说明).zip

    - 通过阅读01源代码.rar中的代码,可以学习到Java Web项目的目录结构、类与接口设计、控制流逻辑等。 - 分析源码有助于深入理解Java Web开发的流程,以及如何将业务需求转化为实际的代码实现。 7. **实战经验** ...

    web初级项目<>

    《员工管理系统》是一个针对初学者设计的Web项目,旨在帮助新手熟悉Java Web开发的基本流程和技术栈...通过实践这个项目,新手可以建立起对Web开发的全面认识,并为后续深入学习和掌握更复杂的Web应用打下坚实的基础。

    暖心家装平台.zip

    【标题】"暖心家装平台.zip"是一个包含源代码和数据库设计的压缩文件,适用于学习、创业、工作、毕业设计或课程设计用途。这个资源提供的内容可能是一个完整的家装服务管理系统的实现,旨在帮助用户管理和优化家装...

    Java项目实战-基于WEB的仓库管理系统的设计与实现(附源码,部署说明).zip

    6. **源码分析**:“05源代码.rar”包含了项目的全部源代码,可以深入学习系统的实现细节,例如控制器类如何响应前端请求,服务层如何处理业务逻辑,以及DAO层如何与数据库交互。这为开发者提供了很好的学习和实践...

    6-应用153-第一组-张龙-个人总结1

    - **技术收获**:学习了多种技术的运用,如JSP、ECharts等,并认识到不同技术在开发效率上的差异。 - **未来发展展望**:计划在未来的团队合作中更加注重成员的实际能力和兴趣,以提高项目成功率和团队满意度。

    百度地图毕业设计源码-https-github.com-czwbig-Tmall_SSH:https-github.com-czwbig-Tm

    了,但如果接手老项目的话还是要懂的,学习过程我们也可以认识到它们当时优秀的设计理念, 当时解决了哪些痛点,后面又是因为什么被新技术替代,这样才能加深对 Java Web 整个平台的理解,不亏。 项目用到的技术如下...

    基于java的CRM客户关系管理系统毕业设计(项目报告+答辩PPT+源代码+部署视频).zip

    了解如何配置和管理Tomcat是项目实施的关键技能之一。 8. **项目报告文档**:这部分内容详细记录了项目的背景、需求分析、系统设计、实现过程以及测试结果,是理解项目全貌的重要资料。 9. **答辩PPT**:通常包含...

    基于SSM的物流管理系统源码.zip

    10. **部署与运维**:源码可能还包括部署说明,讲解如何将系统部署到Tomcat服务器或云平台上,以及如何进行性能监控和日志管理。 总的来说,这个基于SSM的物流管理系统源码是一个全面的学习资源,涵盖了从后端开发...

    网上书店管理系统v.15-8

    《网上书店管理系统v.15-8》是一个典型的毕业设计项目,旨在展示...通过这个项目,学习者可以深入理解Web应用程序的开发流程,掌握Java编程和相关框架的使用,同时对数据库管理、网页设计和系统安全有更全面的认识。

    基于SSM框架的网上花店销售系统(含数据库文件).zip

    在本项目中,控制器类(Controller)会接收用户的请求,调用服务层的方法处理业务,然后返回视图名或Model对象,由SpringMVC决定如何渲染视图。 再者,MyBatis是一个优秀的持久层框架,它支持定制化SQL、存储过程...

    基于Java的源码-电子书店管理系统.zip

    《基于Java的电子书店管理系统详解》 在信息技术日益发达的今天,电子书店管理系统已经成为图书行业不可或...通过学习和理解这些技术,开发者不仅能掌握一个完整的Web应用开发流程,还能对Java技术栈有更深入的认识。

Global site tag (gtag.js) - Google Analytics