zoukankan      html  css  js  c++  java
  • Tomcat加载类机制 我们到底能走多远系列(14)

    我们到底能走多远系列(14)

    扯淡:各位,你们的钱是省出来的吗?前几天一个同事(女)结婚,了解了下情况:男女一起买房接*80*方,实际房子是60*,四五十万的首付吧,加上装潢,男女全家,连外公外婆都出力出钱,搞定,圆满买房结婚啦。大多数的我们都会走这么一段路或已经走了这一段路。我不知道你们听到这样普通的八卦会是怎样的感受。

    刚看完《温州一家人》的前两集,买家送女儿出国学习,带着儿子老婆以捡破烂开始温州的新生活。虽然是电视剧,但上点年纪的都应该知道我们的上一辈很多人都有类似的魄力,对,因为那时候穷,穷怕了,拼了,现在一无所有,输了,还是一无所有,不怕。

    可是现在不一样了,我们稍稍付出点努力就可以温饱,再多付出点,还能接*传说的小康。我们安逸,我们爱安逸,谁想打破安逸?谁也不想。所以我们宁愿背负巨额的按揭,宁愿接过父母的血汗,宁愿丢掉自己的梦想,也要维护住着安逸。

    我也不知道那个是好的那个是不好的,毕竟我也没资格评价,但是这种矛盾是我们大多数人现在要面对的,睿智的你会怎么度过?

    --------------------------------------------------------------------------------------

    前几天利用了下午休时间去了家公司面试,有个问题说:如何来确保你写的模块或方法是高质量的呢?

    我当时说:通过单元测试,什么review,性能测试。

    回答方向不对。

              可读性:代码是否可读易读,对于一个团队来说,编码标准是否一致,编码风格是否一致;

       功能性:代码正确得实现了业务逻辑;

       可维护性:代码逻辑是有层次的,是容易修改的;

       高效性:代码实现在时间和空间的使用上是高效的;

    你会怎么回答呢?

    主题:

    外围知识:

    1,jvm(java virtual mechanism)load class的基本原理 可以参考园子里的文章

    2,关于class.forName(String)
    首先,我们是明白class文件被jvm加载进来后,可以理解成以二进制形式存在内存中,每一个class我们可以通过包名和类名找到它。
    其次,有了new这样的操作,我们为什么还需要这东西做什么呢?
    网上的一段代码解释了这个问题:

    String className = readfromXMlConfig;//从xml 配置文件中获得字符串
    class c = Class.forName(className);
    factory = (ExampleInterface)c.newInstance(); 

    我们可以看到被newInstance出来的实例只要是继承ExampleInterface接口就可以满足了,有一点需要记住的是,如果提供的类没有继承ExampleInterface,就会异常。
    new关键字和newInstance()方法的区别:
    newInstance: 弱类型。低效率。只能调用无参构造。(可是后面的代码看来似乎能够有参构造...)
    new: 强类型。相对高效。能调用任何public构造。

    3,加载类 java.lang.ClassLoader
    我们可以通过调用ClassLoader的loadClass(String)方法来加载自己需要的类。
    例子:

    package code.loader;
    
    import java.net.URL;
    import java.net.URLClassLoader;
    
    import code.mytest.Test2;
    
    public class MyLoader1 {
        public static void main(String[] args) throws Exception {
            // 类名(注意:这里是包含了包名没有.class的形式)
            String className = "code.mytest.Test2";
            // class文件位置
            URL url = new URL("file:/D:/");
            // ClassLoader实例
            ClassLoader loader = new URLClassLoader(new URL[] {url});
            // load进来之后是一个Class实例
            Class c1 = loader.loadClass(className);
            // 利用Class实例得到我们想要的实例
            Test2 test = (Test2)c1.newInstance();
            // 调用新实例的方法
            test.sayYouILY();
        }
    }

    tomcat load class:

    Tomcat需要自己加载web工程中的servlet类,我们频繁发布修改后的工程,Tomcat就需要加载我们提供的代码让他跑起来。
    在Http请求进入tomcat后,tomcat需要根据调用的servlet名把这个类先加载进来,这个过程是由tomcat的内部类:org.apache.catalina.loader.WebappLoader实现的,这个类实现了tomcat的org.apache.catalina.Loader接口,而WebappLoader中引用org.apache.catalina.loader.WebappClassLoader,而WebappClassLoader继承了URLClassLoader


    WebappLoader用getClassLoader()方法向外提供出一个自定义的ClassLoader,为什么这么说呢?来看下源码:
    1,WebappLoader中的start()方法中会调用下面两句:

    classLoader = createClassLoader();//这个是内置的private方法
    classLoader.setResources(container.getResources());

    2,createClassLoader源码:

    private WebappClassLoader createClassLoader()
            throws Exception {
            // 这个loaderClass是啥东西呀?
            // 其实是WebappClassLoader私有变量,定义成:private String loaderClass ="org.apache.catalina.loader.WebappClassLoader";
            Class clazz = Class.forName(loaderClass);
            WebappClassLoader classLoader = null;
    
            if (parentClassLoader == null) {
                parentClassLoader = container.getParentClassLoader();
            }
            Class[] argTypes = { ClassLoader.class };
            Object[] args = { parentClassLoader };
            // API:返回一个 Constructor 对象,它反映此 Class 对象所表示的类的指定公共构造方法。parameterTypes 参数是 Class 对象的一个数组,这些 Class 对象按声明顺序标识构造方法的形式参数类型。
            Constructor constr = clazz.getConstructor(argTypes);
            //使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。(这样做是不是达到了前面提到的有参构造呢)
            classLoader = (WebappClassLoader) constr.newInstance(args);
            return classLoader;
        }

    3,通过WebappClassLoader,可以控制已经加载的类不用再重复操作。
    每一个通过WebappClassLoader加载的类都会被org.apache.catalina.loader.ResourceEntry记录下来

    package org.apache.catalina.loader;
    
    import java.net.URL;
    import java.security.cert.Certificate;
    import java.util.jar.Manifest;
    
    public class ResourceEntry {
        public long lastModified = -1;
        public byte[] binaryContent = null;// 类是以byte数组类型被储存在内存中
        public volatile Class loadedClass = null;
        public URL source = null;// URL名称
        public URL codeBase = null;
        public Manifest manifest = null;
        public Certificate[] certificates = null;
    }

    在WebappClassLoader里有一个Map来记录已经被加载的类

     /**
         * The cache of ResourceEntry for classes and resources we have loaded,
         * keyed by resource name.
         */
        protected HashMap resourceEntries = new HashMap();

    这样一来当需要加载新类是我们先在这个map里面去找一下
    来看一下findResource方法的源码:

    public URL findResource(final String name) {
            if (log.isDebugEnabled())
                log.debug("    findResource(" + name + ")");
            URL url = null;
            if (hasExternalRepositories && searchExternalFirst)
                url = super.findResource(name);
            if (url == null) {
                // 去传说中的内存里找是否已经加载
                ResourceEntry entry = (ResourceEntry) resourceEntries.get(name);
                // 没有加载过
                if (entry == null) {
                    if (securityManager != null) {
                        PrivilegedAction<ResourceEntry> dp =
                            new PrivilegedFindResourceByName(name, name);
                        entry = AccessController.doPrivileged(dp);
                    } else {
                        entry = findResourceInternal(name, name);
                    }
                }
                // 加载过
                if (entry != null) {
                    url = entry.source;
                }
            }
            if ((url == null) && hasExternalRepositories && !searchExternalFirst)
                url = super.findResource(name);
    
            if (log.isDebugEnabled()) {
                if (url != null)
                    log.debug("    --> Returning '" + url.toString() + "'");
                else
                    log.debug("    --> Resource not found, returning null");
            }
            // 返回URL
            return (url);
        }

    总结:

    1,tomcat自定义自己的class loader,值得借鉴,还没有研究核心的实现类,因为还需要学习下自定义loader的实现。

    2,用hashmap来存需要实时查询的资源,很实用。

    让我们继续前行

    ----------------------------------------------------------------------

    努力不一定成功,但不努力肯定不会成功。
    共勉

  • 相关阅读:
    MySQL修改时区的方法小结
    MYSQL日期 字符串 时间戳互转
    2017php经典面试题
    PHP获得真实客户端的真实IP REMOTE_ADDR,HTTP_CLIENT_IP,HTTP_X_FORWARDED_FOR
    开放api接口签名验证
    MySql之ALTER命令用法详细解读(转)
    easyUI datagrid 清空
    webApi文档好帮手-apidoc使用教程
    驼峰命名和下划线命名互转php实现
    SQL Server 数据导入Mysql详细教程
  • 原文地址:https://www.cnblogs.com/killbug/p/2764632.html
Copyright © 2011-2022 走看看