本篇内容介绍了“tomcat与类加载器的介绍”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!
Tomcat是我们从一开始接触Java Web就认识的一个web服务器,它是由Java语言编写的,主要的文件夹就是bin,conf,lib,logs,webapps这5个文件夹
Bin,是我们启动,关闭tomcat的命令所在的文件夹
Conf,是我们tomcat相应配置所在的地方,在我们最早学习部署的时候,就有一种直接在conf中修改server.xml以及session过期时间之类的。
Lib,是tomcat所需依赖所在的文件夹,在之前5.x的版本中,并不直接是lib文件夹,而是分成了3个,common,server和shared三个文件夹,在下面我们来详细说一下这3个被移除掉的东西对于现在的我们还有什么意义。
Logs,是tomcat运行中的日志记录,当我们需要排查线上问题时,很多时候会需要通过查阅日志进行错误定位。
Webapps,这个我们应该是最熟悉的,因为我们部署项目都是扔到webapps里面然后启动tomcat
Tomcat也是使用Java编写的一个web服务器,需要依赖Java环境,也就是说它的运行是需要基于JVM的,而用到JVM也就必然会使用类加载器来进行类加载。
Tomcat的类加载架构如下图所示
灰色背景的即我们之前提到过的JVM中原生的类加载器,分别负责的工作范围大家可以通过上一篇文章回顾。而往下我们可以看到有Conmmon,Catalina,Shared,WebApp,JSP这几种类加载器,在我们讲解每个类加载器的作用前,先说一下tomcat 5.x的架构,便于我们理解。
在tomcat 5.x的时候,并不是一个lib包,而是common,server和shared这三个文件夹,按照双亲委托机制,不同的类加载器负责不同的范围,上图的Common类加载器负责/common/*,Catalina类加载器负责/server/*,Shared类加载器负责/shared/*,而WebAppClassLoader就是我们webapps文件夹下每个项目对应的类加载器了。
/common/*中的类可以被tomcat和所有的web应用程序共同使用。
/server/*中的类可以被tomcat使用,而对web应用程序不可见。
/shared/*中的类可以被web应用程序共同使用,而对tomcat不可见。
看到这里,上面问的第三个问题,如果有10个SSM架构的项目,怎么节约内存。答案就是可以通过把SSM的jar包放到/shared/文件夹中,整个tomcat运行只有这一份,所有web项目共用它来实现。
那么问题又来了,现在没这些文件夹,只有一个lib包了,又该怎么办。
Lib包现在相当于是common,我们把jar包放到common中虽然可以达到效果,但感觉会很混乱,因为这里面有tomcat运行所必须的依赖。所以我们需要把shared用上。
我们打开conf/Catalina.properties文件,可以看到这么一个默认的配置。
其中common.loader已经有值了,而server.loader和shared.loader是没有值的。对应上面我们说到的3个文件夹,想必各位同学应该是知道这每项代表的意思了。
那么我们可以直接在tomcat中建立shared文件夹,将需要共享的jar包放到文件夹中,然后配置shared.loader的值为文件夹的路径,就可以用上了。
在我们开发过程中,如果使用的tomcat(或者其他一些主流的web服务器)和jsp的话,我们习惯于修改jsp之后直接切换到浏览器进行一次刷新,看改动是否生效,并不会去重启项目,这就和我们前面说到的JasperLoader相关了。
WebAppClassLoader是每个web项目有一个,如我们举例所说的10个项目,那么就有10个WebAppClassLoader,但JasperLoader就不止于此了。
我们有一个jsp文件,就有一个JasperLoader。
为什么需要这么多?
我们的JSP,大家都知道本质上是个Servlet,也就是以字节码文件的形式存在。在JSP文件被编译后,我们会得到一个同名_jsp.class文件,然后被类加载器加载到JVM中使用。当服务器检测到JSP被修改后,会替换掉该JSP当前的JasperLoader实例,然后当再次访问到这个JSP页面时,触发它的编译和类加载操作,此时就会再次创建一个JasperLoader用来加载JSP编译得出的字节码文件。
双亲委托机制并不是强制的要求,只是作为一种推荐的方式,在面对一些特殊的情况下,可能需要破坏这种机制。
这里所说的破坏并不见的是一种贬义的行为,换成突破二字可能会更好。
双亲委托机制存在一种缺陷,基础类加载器只能加载自己指定范围内的类,对于用户提供的类是没法加载的,以至于如果基础类需要用到用户类,那就无法加载。比如JDBC,我们都知道这是Java提供了一个规范,由不同的厂商去实现,这个规范通过接口体现,作为基础类,而厂商的实现是驱动包,放在我们项目的依赖包中。
我们可以使用DriverManager.getConnection(url,user,password)直接去获取连接,而不手动去做贾琏欲执事中的第一步,使用Class.forName()去加载驱动。这样一来,DriverClassLoader会自动去寻找并加载驱动。如果严格按照双亲委托机制,DriverManager是java.sql包中的,属于BootStrapClassLoader的范围,它只能看到这个文件夹中的类,而我们的驱动包是不在这里面的,就无法找到类,也就没法去加载了。那实际是怎么处理的呢?
我们可以看到这是getConnection重载的方法,我们直接传入URL,user和password实际就是重载的这个方法。在这里我们可以看到获取了调用者的类加载器,并且判断了callerCL为null。同学们想一下,会有类没有类加载器吗?为null并不是说它没有类加载器,而是因为它的类加载器是BootStrapClassLoader,这是由C++语言写的,无法用Java对象表示。好,既然知道你是BootStrapClassLoader加载的了,那我就给你替换掉,替换成当前线程的线程上下文类加载器。这个类是在线程创建的时候,从父线程继承或者默认直接取ApplicationClassLoader。换成ApplicationClassLoader了,那我们项目依赖的驱动包是不是也就很轻松的可以扫描并加载了?当然,如果你本身调用者所属的类加载器就是Application Class Loader那都不用去线程中取了。
“tomcat与类加载器的介绍”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注亿速云网站,小编将为大家输出更多高质量的实用文章!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。