当前位置: 首页 > news >正文

湘潭自适应网站建设 磐石网络报名窗口网站建设

湘潭自适应网站建设 磐石网络,报名窗口网站建设,夷陵网,做彩票网站制作类加载器 在分析 tomcat 类加载之前#xff0c;我们简单的回顾下 java 体系的类加载器 启动类加载器#xff08;Bootstrap ClassLoader)#xff1a;加载对象是java的核心类库#xff0c;把一些的 java 类加载到 jvm 中#xff0c;它并不是我们熟悉的 ClassLoader#x…类加载器 在分析 tomcat 类加载之前我们简单的回顾下 java 体系的类加载器 启动类加载器Bootstrap ClassLoader)加载对象是java的核心类库把一些的 java 类加载到 jvm 中它并不是我们熟悉的 ClassLoader而是 jvm 层面由 C/C 实现的类加载器负责加载 $JAVA_HOME/jre/lib 目录下 jvm 指定的类库它是无法被 java 应用程序直接使用的扩展类加载器Extension Classloader它是一个 ClassLoader 实例父加载器是启动类加载器它负责加载 $JAVA_HOME/jre/lib/ext 目录的类库应用类加载器Application ClassLoader又叫做系统类加载器(System ClassLoader)负责加载用户类路径-cp参数指定的类库可以通过 ClassLoader.getSystemClassLoader() 获取它也是由启动类加载器加载的自定义类加载器应用程序根据自己的需求开发的类加载器可以继承 ClassLoader当然也可以不继承 下图描述了类加载器的关系图其中自定义类加载器有N多个 我们知道 java.lang.ClassLoader 有双亲委派机制准确的说是单亲因为只有一个parent这只是 java 建议的规范我们也可以不遵循这条规则但是建议遵循该规则。此外有一点需要注意的是类加载器不局限于 ClassLoader我们也可以自己实现一个类加载器只要你加载出来的 Class 符合 jvm 规范即可 我们在日常开发工作中经常会遇到类冲突的情况明明 classpath 下面的类有这个方法但是一旦跑线上环境就出错比如NoSuchMethodError、NoClassDefFoundError、NoClassDefFoundError 等。我们可以使用 jvm 参数 -verbose:class 方便地定位该问题使用该参数可以快速地定位某个类是从哪个jar包加载的而不是一味地埋头苦干求百度找Google。下面是使用 -verbose:class jvm 参数的部分日志输出 [Loaded org.springframework.context.annotation.CommonAnnotationBeanPostProcessor from file:/D:/tomcat/webapps/touch/WEB-INF/lib/spring-context-4.3.7.RELEASE.jar] [Loaded com.alibaba.dubbo.rpc.InvokerListener from file:/D:/tomcat/webapps/touch/WEB-INF/lib/dubbo-2.5.3.jar] 我们有必要了解下关于类加载有几个重要的知识点 在 Java 中我们用完全类名来标识一个类而在 JVM 层面使用完全类名 CloassLoader 对象实例 ID 作为唯一标识因此使用不同实例的类加载器加载的两个同名的类他们的类实例是不同的并且不能强制转换在双亲委派机制中类加载器查找类时是一层层往父类加载器查找的最后才查看自己如果都找不到则会抛出异常而不是一层层往下找的每个运行中的线程都有一个 CloassLoader并且会从父线程中继承默认是应用类加载器在没有显式声明由哪个类加载器加载类时比如 new 关键字将默认由当前线程的类加载器加载该类 由于篇幅有限关于类加载的过程这里不再展开了可以参考厮大的博客 Java虚拟机类加载机制Java虚拟机类加载机制_朱小厮的博客-CSDN博客_加载、验证、准备、初始化、和卸载这5个阶段的顺序是确定的,类的加载 tomcat 类加载器 根据实际的应用场景我们来分析下 tomcat 类加载器需要解决的几个问题 为了避免类冲突每个 webapp 项目中各自使用的类库要有隔离机制不同 webapp 项目支持共享某些类库类加载器应该支持热插拔功能比如对 jsp 的支持、webapp 的 reload 操作 为了解决以上问题tomcat设计了一套类加载器如下图所示。在 Tomcat 里面最重要的是 Common 类加载器它的父加载器是应用程序类加载器负责加载 ${catalina.base}/lib、${catalina.home}/lib 目录下面所有的 .jar 文件和 .class 文件。下图的虚线部分有 catalina 类加载器、share 类加载器并且它们的 parent 是 common 类加载器默认情况下被赋值为 Common 类加载器实例即 Common 类加载器、catalina 类加载器、 share 类加载器都属于同一个实例。当然如果我们通过修改 catalina.properties 文件的 server.loader 和 shared.loader 配置从而指定其创建不同的类加载器 我们先从 Bootstrap 这个入口说起在执行 init 的时候会实例化类加载器在初始化类加载器之后立即设置线程上下文类加载器Thread Context ClassLoader为 catalina 类加载器接下来是为 Catalina 组件指定父类加载器。为什么要设置线程上下文的类加载器呢一方面很多诸如 ClassUtils 之类的编码他们在获取 ClassLoader 的时候都是先尝试从 Thread 上下文中获取 ClassLoader例如ClassLoader cl Thread.currentThread().getContextClassLoader(); 另一方面在没有显式指定类加载器的情况下默认使用线程的上下文类加载器加载类由于 tomcat 的大部分 jar 包都在 ${catalina.hom}/lib 目录因此需要将线程类加载器指定为 catalina 类加载器否则加载不了相关的类。 双亲委派模型存在设计上的缺陷在某些应用场景下例如加载 SPI 实现JNDI、JDBC等如果我们严格遵循双亲委派的一般性原则使用应用程序类加载器由于这些 SPI 实现在厂商的 jar 包中所以应用程序类加载器不可能认识这些代码啊怎么办为了解决这个问题Java 设计团队引入了一个不太优雅的设计Thread Context ClassLoader有了这个线程上下文类加载器我们便可以做一些“舞弊”的事情了JNDI 服务可以使用这个类加载器加载 SPI 需要的代码JDBC、JAXB 也是如此。这样双亲委派模型便被破坏了。 Bootstrap.java public void init() throws Exception {// 初始化commonLoader、catalinaLoader、sharedLoader关于ClassLoader的后面再看initClassLoaders();// 设置上下文类加载器为 catalinaLoader Thread.currentThread().setContextClassLoader(catalinaLoader);SecurityClassLoad.securityClassLoad(catalinaLoader);// 反射方法实例化Catalina后面初始化Catalina用了很多反射不知道意图是什么Class? startupClass catalinaLoader.loadClass(org.apache.catalina.startup.Catalina);Object startupInstance startupClass.getConstructor().newInstance();//TODO 为Catalina对象设置其父加载器为shared类加载器默认情况下就是catalina类加载器// 引用Catalina实例catalinaDaemon startupInstance; catalina.properties 文件的相关配置如下所示 common.loader${catalina.base}/lib,${catalina.base}/lib/*.jar,${catalina.home}/lib,${catalina.home}/lib/*.jar server.loader 我们再来看下创建类加载器的代码首先是创建 common 类加载器从 catalina.properties 中读取 common.loader 配置作为 common 类加载器的路径。我们注意到 common.loader 中存在 ${catalina.base}、${catalina.home} 这样的占位符在读取配置之后tomcat 会进行替换处理同理 server.loader、shared.loader 也可以使用这样的占位符或者系统变量作为占位符有兴趣的童鞋可以参考下 Bootstrap.replace(String str) 源码如果在项目中有相同的场景的话可以直接 copy 该代码。 Bootstrap.javaprivate void initClassLoaders() {try {// 从catalina.properties中读取common.loader配置作为common类加载的路径commonLoader createClassLoader(common, null);if( commonLoader null ) {commonLoaderthis.getClass().getClassLoader();}// 如果未指定server.loader和shared.loader配置则catalina和shared类加载器都是common类加载器catalinaLoader createClassLoader(server, commonLoader);sharedLoader createClassLoader(shared, commonLoader);} catch (Throwable t) {handleThrowable(t);log.error(Class loader creation threw exception, t);System.exit(1);} 接下来我们再来看下 tomcat 是如何创建 common 类加载器的。关键代码如下所示在创建类加载器时会读取相关的路径配置并把路径封装成 Repository 对象然后交给 ClassLoaderFactory 创建类加载器。 Bootstrap.java private ClassLoader createClassLoader(String name, ClassLoader parent)throws Exception {// 从catalina.propeties中读取配置并替换 catalina.home、或者catalina.base或者环境变量String value CatalinaProperties.getProperty(name .loader);value replace(value);// 遍历目录并对路径进行处理ListRepository repositories new ArrayList();String[] repositoryPaths getPaths(value);for (String repository : repositoryPaths) {//TODO 将路径封装成 Repository 对象}return ClassLoaderFactory.createClassLoader(repositories, parent); 我们再进一步对 ClassLoaderFactory 进行分析都是细节上的处理比如利用文件路径构造带有明显协议的 URL 对象例如本地文件的标准 URL 是 file:/D:/app.jar。另外在创建 URLClassLoader 的时候还需要考虑 jdk 对权限控制的影响因此 tomcat 利用 AccessController 创建 URLClassLoader由此可见 tomcat 编码的严谨性。而我们在实际的开发过程中有时候需要自定义类加载器但往往不会考虑权限控制这块所以在对类加载器进行编码时需要注意一下 ClassLoaderFactory.java public static ClassLoader createClassLoader(ListRepository repositories, final ClassLoader parent)throws Exception {SetURL set new LinkedHashSet();if (repositories ! null) {for (Repository repository : repositories) {// 对不同类型的 Repository 对象进行处理将路径转换为URL类型// 因为 URL 类型带有明显的协议比如jar:xxx、file:xxx}}// 将对应的路径组装成 URLfinal URL[] array set.toArray(new URL[set.size()]);// 在创建 URLClassLoader 需要考虑到 AccessController 的影响return AccessController.doPrivileged(new PrivilegedActionURLClassLoader() {public URLClassLoader run() {if (parent null) return new URLClassLoader(array);else return new URLClassLoader(array, parent);}}); }private static URL buildClassLoaderUrl(File file) throws MalformedURLException {String fileUrlString file.toURI().toString();fileUrlString fileUrlString.replaceAll(!/, %21/); // 转换成URL编码return new URL(fileUrlString); OK前面介绍了 tomcat 创建类加载器的过程接下来我们看下 tomcat 类加载器的具体应用场景 WebappClassLoader 在前面我们介绍了 tomcat 类加载器的设计每个 webapp 使用单独的类加载器完成我们开发的 webapp 应用程序的类加载而每一个 webapp 对应一个 WebappClassLoader。tomcat7 默认使用 WebappClassLoader 类加载器而 tomcat8 默认使用 ParallelWebappClassLoader支持并行加载类的特性这也算是 tomcat8 做的一些优化吧而实际上也是利用 jdk 的功能需要同时满足以下两点才支持并行加载类并且一旦注册了并行加载的能力就不能回退了 1、 没有创建调用者的实例 2、 调用者的所有超类除了类对象都是并行注册的 基于上面两点因此ParallelWebappClassLoader 在 static 代码块中注册并行加载机制而它的父类 URLClassLoader 父类也是具有并行能力的关键代码如下所示 public class ParallelWebappClassLoader extends WebappClassLoaderBase {static {boolean result ClassLoader.registerAsParallelCapable();if (!result) {log.warn(sm.getString(webappClassLoaderParallel.registrationFailed));}}// 省略无关代码... WebappClassLoader 的类图如下所示其中 WebappClassLoaderBase 实现了主要的逻辑并且继承了 Lifecycle在 tomcat 组件启动、关闭时会完成资源的加载、卸载操作例如在 start 过程会读取我们熟悉的 /WEB-INF/classes、/WEB-INF/lib 资源并且记录每个 jar 包的时间戳方便重载 jar 包而在组件 stop 的时候会清理已经加载的资源destory 时会显式地触发 URLClassLoader.close()。这个 Lifecycle 真是无处不在啊 单独的类加载器是无法获取 webapp 的资源信息的因此 tomcat 引入了 WebappLoader便于访问 Context 组件的信息同时为 Context 提供类加载的能力支持下面我们分析下 WebappLoader 的底层实现 WebappLoader 我们先来看看 WebappLoader 几个重要的属性内部持有 Context 组件并且有个我们熟悉的 reloadable 参数如果设为 true则会开启类的热加载机制 public class WebappLoader extends LifecycleMBeanBaseimplements Loader, PropertyChangeListener {private WebappClassLoaderBase classLoader null; // 默认使用ParallelWebappClassLoaderprivate Context context null;private String loaderClass ParallelWebappClassLoader.class.getName();private ClassLoader parentClassLoader null; // 父加载器默认为 catalina 类加载器private boolean reloadable false; // 是否支持热加载类private String classpath null; 在 tomcat 中每个 webapp 对应一个 StandardContext在 start 过程便会实例化 WebappLoader并且调用其 start 方法完成初始化包括创建 ParallelWebappClassLoader 实例然后还会启动 Context 的子容器。注意这两个过程都会将线程上下文类加载器指定为 ParallelWebappClassLoader 类加载器在完成 webapp 相关的类加载之后又将线程上下文类加载器设置为 catalina 类加载器。Context 容器的启动过程这里便不再重复了感兴趣的童鞋请查看前面的博文 webapp源码分析 StandardContext.javaprotected synchronized void startInternal() throws LifecycleException {// 实例化 Loader 实例它是 tomcat 对于 ClassLoader 的封装用于支持在运行期间热加载 class if (getLoader() null) {WebappLoader webappLoader new WebappLoader(getParentClassLoader());webappLoader.setDelegate(getDelegate());setLoader(webappLoader); // 使用了读写锁控制并发问题}// 将 Loader 中的 ParallelWebappClassLoader 绑定到当前线程中并返回 catalian 类加载器ClassLoader oldCCL bindThread();try {if (ok) {// 如果 Loader 是 Lifecycle 实现类则启动该 LoaderLoader loader getLoader();if (loader instanceof Lifecycle) {((Lifecycle) loader).start();}// 设置 ClassLoader 的各种属性setClassLoaderProperty(clearReferencesRmiTargets, getClearReferencesRmiTargets());// 省略……// 解除线程上下文类加载器绑定unbindThread(oldCCL);oldCCL bindThread();// 发出 CONFIGURE_START_EVENT 事件ContextConfig 会处理该事件主要目的是加载 Context 的子容器fireLifecycleEvent(Lifecycle.CONFIGURE_START_EVENT, null);// 启动子容器for (Container child : findChildren()) {if (!child.getState().isAvailable()) {child.start();}}}} finally {// Unbinding threadunbindThread(oldCCL);} 而 WebappLoader 在 stop 的时候会销毁 WebappClassLoader并且进行回收促使 jvm 卸载已加载的类 WebappLoader.javaOverride protected void stopInternal() throws LifecycleException {// 省略不相关代码...if (classLoader ! null) {try {classLoader.stop();} finally {classLoader.destroy();}}classLoader null; // help gc
http://www.dnsts.com.cn/news/52108.html

相关文章:

  • 网站优化分析网站建设丿金手指稳定
  • 哪些网站做财金的好如何编写网站建设销售的心得
  • 做网站沈阳本地产品外观设计费用一般多少钱
  • 黄山网站开发网站做下载页面
  • 网站运营的思路答辩ppt模板
  • 智慧政务网站怎么做济宁网站建设第一品牌
  • python网站开发书籍推荐办公室设计报价
  • 如何推广网站方法微信网站开发系统
  • 发布做网站需求qq群wordpress后台登录慢
  • 四线城市网站建设方向及营利点公司的企业邮箱怎么查
  • 福州市建设局内部网站越秀重点场所
  • 提供手机网站建设推荐通化seo招聘
  • 重庆涪陵网站设计公司推荐东莞营销
  • 建设银行广西分行招聘网站在线做行测的网站
  • 重庆做网站嘉兴公司班级网站页面设计
  • 南宁网站推广方案如何做南昌网站设计系统
  • 网站设计是用什么做的网络营销是指什么
  • 网站建设纯免费官网免费咨询劳动仲裁
  • 去外包公司好优化营商环境的意义
  • 越秀定制型网站建设京东商城 网站建设
  • 建设银行短信开通网站如何建设网站论文文献
  • 小型网站建设需要多少钱乌克兰网站服务器
  • 网站排名提升易下拉教程海宁网站开发
  • 国网公司网站重庆门户网站建设
  • 无锡网站制作 高端网站定制多个网站对比表格怎么做
  • 网站推广营销应该怎么做什么主题 wordpress
  • 茂名专业网站制作公司wordpress菜单消失
  • 网站开发需要哪些人软考中级哪个最容易过
  • 社交网站开发难度暂时没有域名怎么做网站
  • 佛山免费网站设计途牛网电子商务网站建设分析