精华内容
下载资源
问答
  • tomcat类加载过程

    2019-09-13 08:23:18
    从哪里开始,到哪里结束 转载于:https://my.oschina.net/ironthrone/blog/1859436

    从哪里开始,到哪里结束

    转载于:https://my.oschina.net/ironthrone/blog/1859436

    展开全文
  • Tomcat 类加载过程

    2015-04-13 18:31:46
    查看 WebappClassLoader 源码,可以总结出 Tomcat类加载过程如下: 1. 先在自己(即当前 WebappClassLoader)的缓存中查找,有则返回,无则继续; 2. 再递归地在 parent 的缓存中查找,有则返回,无则继续; 3...
    以下内容部分来自网络,如有侵权,请站内联系。

    Tomcat 下的工程,使用 WebappClassLoader 进行类的加载。查看 WebappClassLoader 源码,可以总结出 Tomcat 的类加载过程如下:
    1. 先在自己(即当前 WebappClassLoader)的缓存中查找,有则返回,无则继续;
    2. 再递归地在 parent 的缓存中查找,有则返回,无则继续;
    3. 缓存中没有。尝试使用 sun.misc.Launcher$AppClassLoader 加载,成功则返回,失败则继续;
    这样做的目的是,避免工程中重写了 Java 基础类,造成混乱。换句话说, webapp 下的 java.lang.* 等将被委托给上层的类加载器( ExtClassLoader 或 LaunchClassLoader 等)加载,这样就保证了基础类的安全。请放心,这里 sun.misc.Launcher$AppClassLoader 是不会加载到 webapp 中用户自定义的类的:我们知道 sun.misc.Launcher$AppClassLoader 是用来加载 classpath 中的类的,但 tomcat 消除了 classpath 的概念。
    4. 一般情况下,调用自己的 findClass() 方法进行加载,成功则返回,失败则继续;
    5. 若自己加载失败,递归请求 parent 进行加载;
    6. 若最终都没能加载成功,则抛出 ClassNotFoundException 。

    从4、5两步可以看出, Tomcat 的类加载过程和 Java 经典的类加载策略是有出入的,它在一定程度上破坏了双亲委派模型。这样做的目的是为了迎合 Servlet 规范,应用程序类的优先级要高于容器中类的优先级。

    一个 WebappClassLoader 对应着一个 Web 应用程序,其父亲是 Tomcat 的 lib 的类加载器。所以 Tomcat 在默认情况下,不是完全按照先 Tomcat 的 lib 再 Web 应用的 lib 这种顺序去加载类的,正确的 Jar 包加载顺序是:
    1. JRE 中的 Java 基础包;
    2. Web 应用 WEB-INF\lib 下的包;
    3. Tomcat\lib 下的包。
    所以,如果想在 Web 应用间共享一些 Jar 包,不仅需要将公共包放在 Tomcat 的 lib 下,还要删掉 Web 应用 lib 下的包,否则 Tomcat 启动时还是会优先加载 Web 应用 lib 下的包的。
    展开全文
  • 浅析 Tomcat类加载过程

    2019-07-28 02:38:30
    java 类加载器的功能是将 class 加载入内存, tomcat的的应用程序加载过程使 tomcat拥有了在同一个 jvm 中加载管理多个应用的功能. 在介绍 tomcat应用程序加载过程前,我们先简单了解下 java 类加载机制.在Class 类中,...

    java 类加载器的功能是将 class 加载入内存, tomcat的的应用程序加载过程使 tomcat拥有了在同一个 jvm 中加载管理多个应用的功能.

    在介绍 tomcat应用程序加载过程前,我们先简单了解下 java 类加载机制.在Class 类中,我们可以看到Class#getClassLoader 方法,通过这个方法我们可以获取到相应类对应的 ClassLoader 信息.

    我们在应用程序中去输出不同类的 ClassLoader,比如

    public class Test {
      public static void main(String[] args) {
        System.out.println(Test.class.getClassLoader());
        System.out.println(Integer.class.getClassLoader());
      }
    }
    

    输出的结果是

    sun.misc.Launcher$AppClassLoader@18b4aac2
    null
    

    即我们自定义的类的 ClassLoader 为Launcher 中的内部类AppClassLoader,IntegerClassLoader显示为 null, 实际是它也是有ClassLoader 的,所有classLoadernull 的 class 的classLoader 都是bootstrap class loader,其中 java 应用程序中是不可见的. bootstrap class loade 是最底层的 ClassLoader, 它用于加载 jvm内部的 class. 在bootstrap classloader 之下是Launcher$ExtClassLoader, ExtClassLoader用于加载$JAVA_HOME/jre/lib/ext/下的 class; 在 ClassLoader 中,我们可以看到存在 parent 属性, 这里就得提到 ClassLoader 的委派模型,默认情况下,ClassLoader 去加载类时,会先委派给 parent 加载,若 parent 无法加载,再由自身加载,我们可以简单看下ClassLoader#loadClass(String name, boolean resolve)方法(不相关的代码未展示)

    protected Class<?> loadClass(String name, boolean resolve)
          throws ClassNotFoundException {
         //判断 parent 是否为 null, null 即 bootstrap class loader
        if (parent != null) {
          c = parent.loadClass(name, false);
        } else {
          c = findBootstrapClassOrNull(name);
        }
        //如parent 未加载相应的 class,则自身加载
        if (c == null) {
          // If still not found, then invoke findClass in order
          // to find the class.
          c = findClass(name);
        }
        return c;
      }
    

    Launcher$ExtClassLoader之下是Launcher$AppClassLoader,也就是我们上面 Test.class 的 ClassLoader,Launcher$AppClassLoader的 parent 为Launcher$ExtClassLoader.

    默认情况下,我们自已经编写的 Class的 ClassLoader 都是Launcher$AppClassLoader, 但tomcat 下的 webapps 目录下,是可以拥有多个应用的,如果所有类都使用同一个 ClassLoader 加载,无疑会造成应用程序的混乱. 比如两个应用分别使用 springboot2.x和 springboot1.x, 同时存在两个版本的版本冲突问题相应大家都有遇到.

    tomcat 加载类的过程,可从org.apache.catalina.startup.Bootstrap#main入口,在initClassLoaders()方法中,可以看到 tomcat 初始化了三个 ClassLoader. common,server(catalina)与 shared.

    private void initClassLoaders() {
                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);
    }
    

    其中 common 是 shared 与 catalina的 parent,每个 ClassLoader 对应的类范围在$TOMCAT_HOME/conf/catalina.properties配置中可见,

    common.loader="${catalina.base}/lib".....
    server.loader=
    shared.loader=
    

    顾名思义,common 为公共基础ClassLoader,shared 为多个应用的共享的 ClassLoader,Server 为服务器的 ClassLoader.
    在 tomcat 中,继续往下看,我们可以看到在init方法中

    public void init() throws Exception {
    	Thread.currentThread().setContextClassLoader(catalinaLoader);
    	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);
       }
    

    最终的结果是线程上下文的 ClassLoader 为catalinaLoader(serverLoader),CatalinaparentClassLoadersharedClassLoader.(Catalina不是 ClassLoader,只是存储了 parentClassLoader 信息,下面的StandardContext也是)
    然后再看StandardContext#startInternal

    protected synchronized void startInternal() throws LifecycleException {
       WebappLoader webappLoader = new WebappLoader(getParentClassLoader());
       webappLoader.setDelegate(getDelegate());
       setLoader(webappLoader);
    }
    

    这里将 Catalina 中的 parentLoader 放入了 WebAppLoader 中,
    Webapp中定义了属性

    private String loaderClass = ParallelWebappClassLoader.class.getName();
    

    ParallelWebappClassLoader也就是我们应用服务的 ClassLoader,具体可见方法WebAppLoader#createClassLoader()

    private WebappClassLoaderBase createClassLoader()
            throws Exception {
    
            Class<?> clazz = Class.forName(loaderClass);
            WebappClassLoaderBase classLoader = null;
    
            if (parentClassLoader == null) {
                parentClassLoader = context.getParentClassLoader();
            }
            Class<?>[] argTypes = { ClassLoader.class };
            Object[] args = { parentClassLoader };
            Constructor<?> constr = clazz.getConstructor(argTypes);
            classLoader = (WebappClassLoaderBase) constr.newInstance(args);
    
            return classLoader;
        }
    

    创建ParallelWebappClassLoader后,startInternal()中执行了
    classLoader.setResources(context.getResources());
    将 classLoader 与应用中class 绑定.
    Context 与 WebAppLoader 也进行关联.
    这样,不同的 应用有了不同的 classLoader,但他们的 parent 都是 sharedClassLoader,也就是说他们可以共享sharedClassLoader 以上的所有类.

    而后请求过来时,首先经过的不是ParallelWebappClassLoader,因为没有 url 是没法判断是哪个应用的,这时是 catalinaLoader(serverLoader)处理请求,根据请求 url 寻找到对应的 context,再切换至对应应用的ParallelWebappClassLoader,对用户的请求进行处理.
    StandardHostValue#invoke方法可见

    public final void invoke(Request request, Response response)
            throws IOException, ServletException {
    		Context context = request.getContext();
    		context.bind(Globals.IS_SECURITY_ENABLED, MY_CLASSLOADER);
    }
    

    切换 classLoder 的方法可见
    StandardContext#bind

        public ClassLoader bind(boolean usePrivilegedAction, ClassLoader originalClassLoader) {
            Loader loader = getLoader();
            ClassLoader webApplicationClassLoader = null;
            if (loader != null) {
                webApplicationClassLoader = loader.getClassLoader();
            }
            Thread.currentThread().setContextClassLoader(webApplicationClassLoader);
      }
    

    以上即 tomcatClassLoader 加载与切换的简略过程.

    展开全文
  • Tomcat类加载机制

    2018-10-14 19:26:42
    tomcat组成 tomcat主要由以上组建组成. 整个的http请求是通过tomcat以上组件协调完成的,server和service完成请求的... tomcat类加载 下图简述tomcat类加载过程: tomcat的启动方式都是通过在org.apache.catalina.st...

    tomcat组成 在这里插入图片描述

    tomcat主要由以上组建组成.
    整个的http请求是通过tomcat以上组件协调完成的,server和service完成请求的基本设施,connector通过指定的协议和端口监听用户的请求,通过一层一层的处理,最终封装好响应信息返回给客户端.


    tomcat类加载

    下图简述tomcat类加载过程:
    在这里插入图片描述

    tomcat的启动方式都是通过在org.apache.catalina.startup.BootStrap类中的main方法中启动完成的.
    首先会加载JVM运行所需的类,以及一些扩展类,然后加载tomcat系统运行所需的类,然后就是开始加载webapp应用对应的class文件以及lib.tomcat在启动的过程中回去解析对应配置文件的server.xml以及各web应用的web.xml,首先就会解析的,解析成键值对.。
    web应用在启动的时候,都会创建一个上下文serveletContext,把解析成键值对以后,封装到上下文之中。


    Tomcat 如何实现自己独特的类加载机制?

    所以,Tomcat 是怎么实现的呢?牛逼的Tomcat团队已经设计好了。我们看看他们的设计图:
    在这里插入图片描述

    我们看到,前面3个类加载和默认的一致,CommonClassLoader、CatalinaClassLoader、SharedClassLoader和WebappClassLoader则是Tomcat自己定义的类加载器,它们分别加载/common/、/server/、/shared/*(在tomcat 6之后已经合并到根目录下的lib目录下)和/WebApp/WEB-INF/*中的Java类库。其中WebApp类加载器和Jsp类加载器通常会存在多个实例,每一个Web应用程序对应一个WebApp类加载器,每一个JSP文件对应一个Jsp类加载器。

    commonLoader:Tomcat最基本的类加载器,加载路径中的class可以被Tomcat容器本身以及各个Webapp访问;
    catalinaLoader:Tomcat容器私有的类加载器,加载路径中的class对于Webapp不可见;
    sharedLoader:各个Webapp共享的类加载器,加载路径中的class对于所有Webapp可见,但是对于Tomcat容器不可见;
    WebappClassLoader:各个Webapp私有的类加载器,加载路径中的class只对当前Webapp可见;

    从启动类BootStrap中的源码也可以看出来:

    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) {
                handleThrowable(t);
                log.error("Class loader creation threw exception", t);
                System.exit(1);
            }
        }
    

    从图中的委派关系中可以看出:

    CommonClassLoader能加载的类都可以被Catalina ClassLoader和SharedClassLoader使用,从而实现了公有类库的共用,而CatalinaClassLoader和Shared ClassLoader自己能加载的类则与对方相互隔离。

    WebAppClassLoader可以使用SharedClassLoader加载到的类,但各个WebAppClassLoader实例之间相互隔离。

    而JasperLoader的加载范围仅仅是这个JSP文件所编译出来的那一个.Class文件,它出现的目的就是为了被丢弃:当Web容器检测到JSP文件被修改时,会替换掉目前的JasperLoader的实例,并通过再建立一个新的Jsp类加载器来实现JSP文件的HotSwap功能。

    好了,至此,我们已经知道了tomcat为什么要这么设计,以及是如何设计的,那么,tomcat 违背了java 推荐的双亲委派模型了吗?答案是:违背了。 我们前面说过:

    双亲委派模型要求除了顶层的启动类加载器之外,其余的类加载器都应当由自己的父类加载器加载。

    很显然,tomcat 不是这样实现,tomcat 为了实现隔离性,没有遵守这个约定,每个webappClassLoader加载自己的目录下的class文件,不会传递给父类加载器。

    我们扩展出一个问题:如果tomcat 的 Common ClassLoader 想加载 WebApp ClassLoader 中的类,该怎么办?
    看了前面的关于破坏双亲委派模型的内容,我们心里有数了,我们可以使用线程上下文类加载器实现,使用线程上下文加载器,可以让父类加载器请求子类加载器去完成类加载的动作。牛逼吧。


    WebAppClassLoader源码分析

    下图是一个概况的流程图, 简介起见,绘图过程中移除了缓存功能.
    在这里插入图片描述
    下面是关键的代码,已经添加了注释.

    //WebappClassLoader.loadClass (Tomcat 7.0)  
        @Override  
        public synchronized Class<?> loadClass(String name, boolean resolve)  
            throws ClassNotFoundException {  
      
            if (log.isDebugEnabled())  
                log.debug("loadClass(" + name + ", " + resolve + ")");  
            Class<?> clazz = null;  
      
            //检查当前ClassLoad是否已经停止了  
            // Log access to stopped classloader  
            if (!started) {  
                try {  
                    throw new IllegalStateException();  
                } catch (IllegalStateException e) {  
                    log.info(sm.getString("webappClassLoader.stopped", name), e);  
                }  
            }  
       
           //检查缓存1,Class是否已经被当前Class"实例"装载过  
           // (0) Check our previously loaded local class cache  
            clazz = findLoadedClass0(name);  
            if (clazz != null) {  
                if (log.isDebugEnabled())  
                    log.debug(" Returning class from cache");  
                if (resolve)  
                    resolveClass(clazz);  
                return (clazz);  
            }  
            //ClassLoader检查缓存2,Class是否已经被父类"实例"装载过  
            // (0.1) Check our previously loaded class cache  
            clazz = findLoadedClass(name);  
            if (clazz != null) {  
                if (log.isDebugEnabled())  
                    log.debug(" Returning class from cache");  
                if (resolve)  
                    resolveClass(clazz);  
                return (clazz);  
            }  
      
            //ClassLoader装载系统classpath下面的类, 阻止webapp覆盖了J2SE的类  
            // (0.2) Try loading the class with the system class loader, to prevent  
            // the webapp from overriding J2SE classes  
            try {  
                clazz = system.loadClass(name);  
                if (clazz != null) {  
                    if (resolve)  
                        resolveClass(clazz);  
                    return (clazz);  
                }  
            } catch (ClassNotFoundException e) {  
                // Ignore  
            }  
      
            //安全检查  
            // (0.5) Permission to access this class when using a SecurityManager  
            if (securityManager != null) {  
                int i = name.lastIndexOf('.');  
                if (i >= 0) {  
                    try {  
                        securityManager.checkPackageAccess(name.substring(0,i));  
                    } catch (SecurityException se) {  
                        String error = "Security Violation, attempt to use " +  
                            "Restricted Class: " + name;  
                        log.info(error, se);  
                        throw new ClassNotFoundException(error, se);  
                    }  
                }  
            }  
      
    		//默认的返回值为false,   
    		//delegate = false  
    		//filter(name) 通过一个List指定哪些package里面的Class使用双亲委派模式,但是默认这个List是空  
    		//比如包名以Java开头的,就需要使用双亲委派模式,不过这一点"部分"由 (0.2) 在上面已经实现了  
            boolean delegateLoad = delegate || filter(name);  
      
            //针对一些明确指定的package,使用双亲委派模式加载  
            // (1) Delegate to our parent if requested  
            if (delegateLoad) {  
                if (log.isDebugEnabled())  
                    log.debug(" Delegating to parent classloader1 " + parent);  
                ClassLoader loader = parent;  
                if (loader == null)  
                    loader = system;  
                try {  
                    clazz = Class.forName(name, false, loader);  
                    if (clazz != null) {  
                        if (log.isDebugEnabled())  
                            log.debug(" Loading class from parent");  
                        if (resolve)  
                            resolveClass(clazz);  
                        return (clazz);  
                    }  
                } catch (ClassNotFoundException e) {  
                    // Ignore  
                   //如果(1)通过双亲委派加载发生异常, 忽略异常,  
                }  
            }  
      
           
            //尝试从本地加载Class( 如果(1)通过双亲委派加载发生异常, 忽略异常,也会走到这一步)  
            // (2) Search local repositories  
            if (log.isDebugEnabled())  
                log.debug(" Searching local repositories");  
            try {  
                clazz = findClass(name);  
                if (clazz != null) {  
                    if (log.isDebugEnabled())  
                        log.debug(" Loading class from local repository");  
                    if (resolve)  
                        resolveClass(clazz);  
                    return (clazz);  
                }  
            } catch (ClassNotFoundException e) {  
                // Ignore  
            }  
      
            //最后依旧没有成功, 忽略所有配置,再次尝试一下双亲加载模式加载  
            // (3) Delegate to parent unconditionally  
            if (!delegateLoad) {  
                if (log.isDebugEnabled())  
                    log.debug(" Delegating to parent classloader at end: " + parent);  
                ClassLoader loader = parent;  
                if (loader == null)  
                    loader = system;  
                try {  
                    clazz = Class.forName(name, false, loader);  
                    if (clazz != null) {  
                        if (log.isDebugEnabled())  
                            log.debug(" Loading class from parent");  
                        if (resolve)  
                            resolveClass(clazz);  
                        return (clazz);  
                    }  
                } catch (ClassNotFoundException e) {  
                    // Ignore  
                }  
            }  
      
            //最后如果依旧没有成功,则抛出异常  
            throw new ClassNotFoundException(name);  
      
        }  
    

    每个应用在部署后,都会创建一个唯一的WebAppClassLoader类加载器。该类加载器会加载位于 WEB-INF/lib下的jar文件中的class 和 WEB-INF/classes下的class文件。

    当应用需要到某个类时,则会按照下面的顺序进行类加载:

    1 使用bootstrap引导类加载器加载

    2 使用system系统类加载器加载

    3 使用应用类加载器在WEB-INF/classes中加载

    4 使用应用类加载器在WEB-INF/lib中加载

    5 使用common类加载器在CATALINA_HOME/lib中加载


    理解Tomcat的classpath

    在很多Apache Tomcat用户论坛,一个问题经常被提出,那就是如何配置Tomcat的classpath,使得一个web应用程序能够找到类或者jar文件,从而可以正常工作。就像许多困扰Tomcat新用户的问题一样,这个问题也很容易解决。在这篇文章中,我们将会介绍Tomcat是如何产生和利用classpath的,然后一个一个解决大多数常见的与classpath相关的问题。

    为什么Classpaths给Tomcat用户带来了麻烦

    一个classpath就是一个参数,来告诉java虚拟机在哪里可以找到类和包去运行一个程序。classpath总是在程序源码外设置的,将其同程序分开可以允许java代码以一种抽象的方式来引用类和包,允许程序可以在任何系统上被配置。为什么那些很有经验的java用户,他们已经非常清楚classpath是什么了,但是在Tomcat运行程序的时候,还是会遇到这样那样的问题呢?

    可能有以下三个原因:
    1、Tomcat处理classpaths的方式与其他java程序是不同的
    2、Tomcat的版本不同,其处理classpaths的方式也可能不同
    3、Tomcat的文档和默认的配置要求以一种特定的方式来完成某些事情。如果你不遵循这种方式,那么你就会陷入麻烦之中。关于如何解决常见的classpath问题,没有信息可以提供,比如外部依赖,共享依赖或者多版本的相同依赖。

    Tomcat的Classpath如何不同于标准的Classpath

    Apache Tomcat目的是尽可能的独立,直观和自动化,为了有效的管理,标准化web应用程序的配置和部署过程,为了安全和命名控件的考虑,限制对不同库的访问。这就是为什么不使用java classpath环境变量的原因了,java的classpath是声明依赖类库默认的地方,Tomcat的start脚本忽略了这个变量,在创建Tomcat系统classloader的时候产生了自己的classpaths。linux系统下在startup.sh中调用了 EXECUTABLE=catalina.sh 。catalina.sh中指定CLASS_PATH的shell代码如下

    # Add on extra jar files to CLASSPATH
    179 if [ ! -z "$CLASSPATH" ] ; then
    180   CLASSPATH="$CLASSPATH":
    181 fi
    182 CLASSPATH="$CLASSPATH""$CATALINA_HOME"/bin/bootstrap.jar
    183 
    184 if [ -z "$CATALINA_OUT" ] ; then
    185   CATALINA_OUT="$CATALINA_BASE"/logs/catalina.out
    186 fi
    187 
    188 if [ -z "$CATALINA_TMPDIR" ] ; then
    189   # Define the java.io.tmpdir to use for Catalina
    190   CATALINA_TMPDIR="$CATALINA_BASE"/temp
    191 fi
    192 
    193 # Add tomcat-juli.jar to classpath
    194 # tomcat-juli.jar can be over-ridden per instance
    195 if [ -r "$CATALINA_BASE/bin/tomcat-juli.jar" ] ; then
    196   CLASSPATH=$CLASSPATH:$CATALINA_BASE/bin/tomcat-juli.jar
    197 else
    198   CLASSPATH=$CLASSPATH:$CATALINA_HOME/bin/tomcat-juli.jar
    199 fi
    200 
    201 # Bugzilla 37848: When no TTY is available, don't output to console
    202 have_tty=0
    203 if [ "`tty`" != "not a tty" ]; then
    204     have_tty=1
    205 fi
    206 
    207 # For Cygwin, switch paths to Windows format before running java
    208 if $cygwin; then
    209   JAVA_HOME=`cygpath --absolute --windows "$JAVA_HOME"`
    210   JRE_HOME=`cygpath --absolute --windows "$JRE_HOME"`
    211   CATALINA_HOME=`cygpath --absolute --windows "$CATALINA_HOME"`
    212   CATALINA_BASE=`cygpath --absolute --windows "$CATALINA_BASE"`
    213   CATALINA_TMPDIR=`cygpath --absolute --windows "$CATALINA_TMPDIR"`
    214   CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
    215   JAVA_ENDORSED_DIRS=`cygpath --path --windows "$JAVA_ENDORSED_DIRS"`
    216 fi
    

    启动Tomcat的命令如下

    439     eval "\"$_RUNJAVA\"" $LOGGING_MANAGER $JAVA_OPTS \
    440     -Djava.endorsed.dirs="\"$JAVA_ENDORSED_DIRS\"" -classpath "\"$CLASSPATH\"" \
    441     -Dcatalina.base="\"$CATALINA_BASE\"" \
    442     -Dcatalina.home="\"$CATALINA_HOME\"" \
    443     -Djava.io.tmpdir="\"$CATALINA_TMPDIR\"" \
    444     org.apache.catalina.startup.Bootstrap "$@" stop
    

    我们可以使用**System.getProperty(“java.class.path”);**来查看一下这个变量

    ++--/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/charsets.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/deploy.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/ext/cldrdata.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/ext/dnsns.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/ext/jaccess.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/ext/jfxrt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/ext/localedata.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/ext/nashorn.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/ext/sunec.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/ext/sunjce_provider.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/ext/sunpkcs11.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/ext/zipfs.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/javaws.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/jce.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/jfr.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/jfxswt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/jsse.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/management-agent.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/plugin.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/resources.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/rt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/lib/ant-javafx.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/lib/dt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/lib/javafx-mx.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/lib/jconsole.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/lib/packager.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/lib/sa-jdi.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/lib/tools.jar:/Users/chenfeihao/Desktop/tomcat_source/apache-tomcat-7.0.67-src/target/classes:/Users/chenfeihao/.m2/repository/org/apache/ant/ant/1.7.0/ant-1.7.0.jar:/Users/chenfeihao/.m2/repository/org/apache/ant/ant-launcher/1.7.0/ant-launcher-1.7.0.jar:/Users/chenfeihao/.m2/repository/ant/ant-apache-log4j/1.6.5/ant-apache-log4j-1.6.5.jar:/Users/chenfeihao/.m2/repository/ant/ant-commons-logging/1.6.5/ant-commons-logging-1.6.5.jar:/Users/chenfeihao/.m2/repository/wsdl4j/wsdl4j/1.6.2/wsdl4j-1.6.2.jar:/Users/chenfeihao/.m2/repository/javax/xml/rpc/javax.xml.rpc-api/1.1/javax.xml.rpc-api-1.1.jar:/Users/chenfeihao/.m2/repository/org/eclipse/jdt/core/compiler/ecj/4.5.1/ecj-4.5.1.jar:/Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar:/Users/chenfeihao/Library/Caches/IntelliJIdea2018.1/captureAgent/debugger-agent.jar
    

    指定了Tomcat的classpath的是这一部分

    /Users/chenfeihao/Desktop/tomcat_source/apache-tomcat-7.0.67-src/target/classes:(我这里直接使用的Tomcat的源码所以相关class文件会被编译到target的classes目录下)
    

    值得一提的是common,catalina与shared的类型都是StandardClassLoader而这个ClassLoader的parent是AppClassLoader,所以也不难理解为什么可以加载到classpath路径下的class文件了。

    想要理解Tomcat如何处理classpath的,看看以下Tomcat6的启动过程:

    1、java虚拟机的bootstrap loader加载java的核心类库。java虚拟机使用JAVA_HOME环境变量来定位核心库的位置。
    2、Startup.sh使用start参数调用Catalina.sh,重写系统的classpath并加载bootstrap.jar和tomcat-juli.jar。这些资源仅对Tomcat可见。
    3、为每一个部署的Context创建Class loader,加载位于每个web应用程序WEB-INF/classes和WEB-INF/lib目录下的所有类和jar文件。每个web应用程序仅仅可见自己目录下的资源。
    4、Common class loader加载位于$CATALINA_HOME/lib目录下的所有类和jar文件,这些资源对所有应用程序和Tomcat可见。

    Tomcat的类加载机制是如何在不同版本之间变化的

    之前的Tomcat版本,其类加载机制有一些不同

    Tomcat 4.x以及更早的版本,一个server loader负责加载Catalina类,现在由commons loader负责了。
    Tomcat 5.x,一个shared loader负责加载在应用程序间共享的类,这些类位于$CATALINA_HOME/shared/lib。在Tomcat6中,这种方式被取消了。
    Tomcat 5.x也包括了一个Catalina loader,加载所有的Catalina组件,现在也被Common loader取代了。

    当你不能按照Tomcat要求的方式做事的时候,怎么办

    如果你使用Tomcat文档推荐的方式做事,你不应该有关于classpath的问题。你的wars包含了所有库和包的复本,你没有任何理由去在多个应用程序间共享一个jar文件,你不需要调用任何外在的资源,你也将不会遇到复杂的情况,例如一个web应用程序运行的时候需要一个jar文件的多个版本。但是如果你确实不能按照推荐的方式来做的时候,一个文件可以解决你所有的问题:catalina.properties。

    使用catalina.properties来配置Tomcat Classpath

    对于那些不想使用默认来加载方式的用户来说,幸运的是,Tomcat的classpath选项不是硬编码的,它们是从$CATALINA_HOME/conf/catalina.properties文件中读取的。

    这个文件包含了除bootstrap和system loader之外的所有其他的loaders,检查这个文件,你会有一些新发现:
    1、Server以及Shared loader还没有被删除,如果它们的属性没有定义,Commons loader负责处理。
    2、被各种loaders加载的类和jar文件不会被自动加载,它们只是用一个简单的通配符语法指定为一组。
    3、这里没有说你不能指定一个额外的仓库,事实上就是说你是可以的。

    server loader不应该改动,但是shared loader还是有许多有用的应用。(shared loader将会在启动过程的最后阶段加载它的类,在Commons loader之后)。现在让我们看看一些常见的问题以及如何解决。

    问题、解决方案和最佳实践

    问题:我的应用程序依赖一个外部的仓库,我不能引用它。
    让Tomcat知道一个外部的仓库,在catalina.properties文件的shared loader位置,使用正确的语法,声明这个仓库。语法基于你要配置的文件或仓库的类型:
    1、增加一个文件夹作为类仓库,使用“path/to/foldername”
    2、增加一个文件夹下的所有jar文件作为类仓库,使用"path/to/foldername/*.jar"
    3、增加单个jar文件作为类仓库,使用"file://path/to/foldername/jarname.jar"
    4、调用环境变量,使用{}格式,例如{VARIABLE_NAME}
    5、声明多个资源,用逗号分隔开
    6、所有的路径相对于CATALINA_BASE或CATALINA_HOME,或者是绝对路径

    问题:我想多个应用程序共享一个jar文件,这个jar文件在Tomcat里面。
    除了一些常见的第三方库(比如JDBC drivers),最好不要在$CATALINA_HOME/lib目录下包含额外的库,即使这样在一些情况下是可行的。应该重新创建比如/shared/lib和/shared/classes目录,然后在catalina.properties配置shared.loader属性:
    “shared/classes,shared/lib/*.jar”

    问题:除了另一个框架,我在一个应用中使用了一个嵌入式Tomcat server,当我访问框架组件的时候遇到classpath errors。
    这个问题好像超出了这篇文章的范畴,但是作为一个常见的classpath相关的问题,这里对如何引起你的错误作一个简单的介绍。
    当嵌入到包含另外核心框架(Wicket或者Spring)的应用中时,Tomcat将在启动框架的时候,使用System classloader加载核心类,而不是从应用的WEB-INF/lib目录下加载。
    java的类加载是懒加载,就是说请求某个类的第一个加载器拥有这个类剩下的生命周期。如果System classloader,它的类对web应用是不可见的,首先加载了框架相关的类,java虚拟机将会阻止来的其他实例被创建,这样就引起了classpath错误。
    解决这个问题的一种方式就是增加一个自定义的bootstrap classloader,使得这个classloader加载合适的库,然后对程序剩下部分的启动正常对待。

    问题:我使用一个标准的应用程序,程序的war包含了依赖的所有包,但是我仍然遇到类定义错误。
    这个问题可能是由许多事情引起的,包括编译或部署过程不是很正确,但是最有可能是web应用程序的目录结构不对造成的。
    java命名转换要求类名映射到存放这个类的目录结构。例如,一个类com.mycompany.mygreat.class需要被存放到目录WEB-INF/classes/com/mycompany/。
    经常代码中丢失一个点号就会引起classpath相关的问题。

    问题:我的web应用程序的不同模块需要使用同一个jar包的两个不同版本。
    这种情况常常出现在一个应用程序中使用多个web框架,这些web框架依赖一个库的不同版本。
    有几种解决方案,但是它们都和classpath不相关。我们在这里说这个问题,是因为一些用户试图在框架的jar文件中的Manifest文件中指定依赖的库的不同版本的classpath,来解决这个问题。
    这种方式在一些web应用服务器中是支持的,所以一些用户想要在Tomcat中也使用这种方式。但是在Manifest文件中指定classpath在Tomcat中是不支持的,这也不是Servlet说明的一部分。
    有以下四种方式来解决这个问题:
    1、你可以更新框架的版本,如果这里能够使得与其他框架依赖相同的版本。
    2、你可以创建两个或更多的自定义classloaders,每个jar文件一个,在WEB/INF/context.xml文件中配置,创建你所需要的两个不同版本的类的实例。
    3、你可以使用jarjar工具将框架和它依赖的库打包成一个jar文件,那么它们会一起被加载。
    4、如果你发现你每隔一天就要处理这种情况,你应该考虑使用OSGI框架,这个框架有许多方法专门就是用来处理这种一个类的多个版本需要运行在同一个java虚拟机里的情况的。

    最佳实践

    1、避免在Tomcat里使用Commons loader加载不属于Tomcat标准发布的库和包,这可能会引起兼容错误。如果你需要在多个应用程序间共享一个库或包,创建shared/lib和shared/classes目录,然后配置到catalina.properties文件的Shared loader。
    2、以上规则的一个例外就是常用的第三方共享库,例如JDBC driver。这些应该放到$CATALINA_HOME/lib目录下。
    3、可能的话,尽可能按照Tomcat的开发者推荐的方式使用Tomcat,如果你发现你不得不频繁配置classpath,你可能需要重新考虑你的开发过程了。

    展开全文
  • 一、java类加载过程(双亲委派模型) 我们先来看一张图片,有助于理解类加载器与类加载过程。 概念 虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接时候...
  • Tomcat类加载

    2019-05-17 08:30:22
    Tomcat类加载设计 类载入:1 new隐式载入,2 显示载入loadclass() 我们都知道:Java默认的类加载机制是委派模式,委派的过程如下。 1)从缓存中加载 2) 如果没有,则从父加载器中加载 3) 如果父加载器没有,从当前...
  • Tomcat类加载

    2020-04-21 18:27:40
    Tomcat类加载器 Tomcat的类加载机制是违反了双亲委托原则的,对于一些未加载的非基础类(Object,String等),各个web应用自己的类加载器(WebAppClassLoader)会优先加载,加载不到时再交给commonClassLoader走双亲委托...
  • Tomcat类加载机制 Tomcat启动时类加载过程 打破双亲委托机制的桎梏 问题扩展 阿里面试 类加载 在JVM中并不是一次性把所有的文件都加载,按时按需加载,比如JVM启动时,会通过不同的类加载器加载不同的类。当...
  • tomcat类加载

    2018-08-16 17:20:52
    JVM类加载器 父类委托机制 父类委托机制 加载器层级关系: CustomClassLoader -&gt; AppClassLoader(应用类) -&gt; ExtClassloader(jdk/jre/lib/ext) -&gt; BootstrapClassLoader(JVM需要的类) ...
  • 加载是类加载过程中的一个阶段,这个阶段会在内存中生成一个代表这个类的java.lang.Class对 象,作为方法区这个类的各种数据的入口。 2.验证 这一阶段的主要目的是为了确保Class文件的字节流中包含的信息是否符合...
  • tomcat启动时很重要的是使用了自己定义的一个类加载器,对于该类加载器的源码稍微...特别说明一下commonLoader的加载过程,首先查找catalina.properties中是否有common.loader的配置项,如有,则将该值作为类加载需...
  • tomcat类加载体系

    2017-05-21 17:16:00
    最近开始读tomcat的源码,主线路当然是类加载机制,在这个过程中有豁然开朗的感觉。这一篇主要是自己的体会,而不是从头到尾的详细解读。很显然,是因为我懒。有多懒呢,懒到把女朋友都弄丢了,哎。 言归正传,从...
  • 标题java类加载过程 标题加载 ->验证 ->准备 -> 解析 ->初始化 加载:将class文件解析成二进制文件进入jvm中 验证:校验文件是否符合java规范,是否会侵害jvm 准备:为类的静态变量赋值默认初始值(如:...
  • 图解Tomcat类加载机制(阿里面试题) Tomcat的类加载机制是违反了双亲委托原则的,对于一些未加载的非基础类(Object,String等),各个web应用自己的类加载器(WebAppClassLoader)会优先加载,加载不到时再交给...
  • Tomcat启动加载过程

    2017-03-24 15:20:40
    我 是在看jvm类加载机制时突然想到我们经常使用的tomcat是如何启动?如何加载类的?于是顺藤摸瓜找了找,和大家分享下,比较粗糙,不要介意。 1、start.bat是启动tomcat的脚本,查看脚本可知其实是启动catalina.bat...
  • tomcat启动加载过程

    2017-03-24 15:27:12
     我 是在看jvm类加载机制时突然想到我们经常使用的tomcat是如何启动?如何加载类的?于是顺藤摸瓜找了找,和大家分享下,比较粗糙,不要介意。 1、start.bat是启动tomcat的脚本,查看脚本可知其实是启动catalina....
  • 类加载机制----------------------------------------形成JVM可以直接使用的Java类型的过程 重要判断Class是否相等(类加载器必须是同一个),要看2个准则 Class clazz1; Class clazz2; (clazz1==clazz2&&...
  • 图解Tomcat类加载机制

    2019-11-21 17:17:52
    Tomcat类加载机制是违反了双亲委托原则的,对于一些未加载的非基础类(Object,String等),各个web应用自己的类加载器(WebAppClassLoader)会优先加载,加载不到时再交给commonClassLoader走双亲委托。 对于JVM来说...
  • Tomcat学习积累笔记—Tomcat类加载机制 1. Java类(.java)—> 字节码⽂件(.class) —> 字节码⽂件需要被加载到jvm内存当中(这个过程就是⼀个类加载的过程) 2. 在类加载机制中有个很重要的角色:类加载器...
  • jvm & tomcat 类加载

    2020-05-15 10:51:24
     1 用户自己的类加载器,把加载请求传给父加载器,父加载器再传给其父加载器,一直到加载器树的顶层。  2 最顶层的类加载器首先针对其特定的位置加载,如果加载不到就转交给子类。  3 如果一直到底层的类加载都...
  • 一、类的加载过程 Java类的加载分为三分阶段:装载类,链接类,类的初始化,其中链接又经历验证、准备、解析三个过程。 1) 装载:类加载器在对应的类缓存空间中查找类并加载类的二进制数据。 1.通过类的全限定类名...
  • 网上很多资料(包括官方文档)都说有common、system这些类加载器。但是我调试源码的时候并没有看到这些类加载器。 tomcat version:8.5.42 本文研究两个问题。 1、JAVA_HOME/lib/ext和/WebApp/WEB-INF/lib中有相同全...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 626
精华内容 250
关键字:

tomcat类加载过程