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

防爆玻璃门网站建设重庆seo排名方法

防爆玻璃门网站建设,重庆seo排名方法,wordpress主页源文件,佛山外发加工网转载自 违反ClassLoader双亲委派机制三部曲第二部——Tomcat类加载机制 前言#xff1a; 本文是基于 ClassLoader双亲委派机制源码分析 了解过正统JDK类加载机制及其实现原理的基础上#xff0c;进而分析这种思想如何应用到Tomcat这个web容器中#xff0c;从源码的角度对 违…转载自 违反ClassLoader双亲委派机制三部曲第二部——Tomcat类加载机制 前言 本文是基于 ClassLoader双亲委派机制源码分析 了解过正统JDK类加载机制及其实现原理的基础上进而分析这种思想如何应用到Tomcat这个web容器中从源码的角度对 违反ClassLoader双亲委派机制三部曲之首部——JDBC驱动加载 中提出的Tomcat是如何完成多个web应用之间相互隔离又如何保证多个web应用都能加载到基础类库的问题进行了解答我们按如下的思路布局整篇文章 先给出Tomcat整体的类加载体系结构通过查看源码验证该类加载体系的正确性总结Tomcat如何设计保证多应用隔离 另外本文是基于Tomcat7的源码进行分析的因此读者最好先搭建一套基于Tomcat7的环境以便查阅源码以及运行调试可以按照该文章的方式进行搭建Tomcat源码导入Idea Tomcat类加载体系结构 图1. Tomcat整体类加载体系结构 Tomcat本身也是一个java项目因此其也需要被JDK的类加载机制加载也就必然存在引导类加载器、扩展类加载器和应用(系统)类加载器。Tomcat自身定义的类加载器主要由图中下半部分组成Common ClassLoader作为Catalina ClassLoader和Shared ClassLoader的parent而Shared ClassLoader又可能存在多个children类加载器WebApp ClassLoader一个WebApp ClassLoader实际上就对应一个Web应用那Web应用就有可能存在Jsp页面这些Jsp页面最终会转成class类被加载因此也需要一个Jsp的类加载器就是图中的JasperLoder 需要注意的是在代码层面Catalina ClassLoader、Shared ClassLoader、Common ClassLoader对应的实体类实际上都是URLClassLoader或者SecureClassLoader一般我们只是根据加载内容的不同和加载父子顺序的关系在逻辑上划分为这三个类加载器而WebApp ClassLoader和JasperLoader都是存在对应的类加载器类的 下面我们从源码设计的角度验证图中类加载器的设计 源码分析Tomcat类加载机制 Tomcat的启动入口在Bootstrap.class中 图2. Tomcat启动入口 其中初始化类加载器的流程在bootstrap.init();中如下“代码清单1” public void init()throws Exception{// Set Catalina pathsetCatalinaHome();setCatalinaBase();// (1) 初始化 classLoaderinitClassLoaders();Thread.currentThread().setContextClassLoader(catalinaLoader);SecurityClassLoad.securityClassLoad(catalinaLoader);// Load our startup class and call its process() methodif (log.isDebugEnabled())log.debug(Loading startup class);//加载 org.apache.catalina.startup.Catalina classClass? startupClass catalinaLoader.loadClass(org.apache.catalina.startup.Catalina);// (2) 实例化 Catalina 实例Object startupInstance startupClass.newInstance();// Set the shared extensions class loaderif (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;}(1)处注释的代码主要进行类加载的初始化以及形成类加载器的关系初始化继续跟进 图3. initClassLoaders()方法 这里红线处的代码实际上创建了三个ClassLoader对象其名称和Tomcat类加载关系图中的类加载器高度一致那么我们猜测createClassLoader(String,ClassLoader)方法可能就是创建Tomcat自定义类加载器的方法之一继续往下看 “ 代码清单2 ” private ClassLoader createClassLoader(String name, ClassLoader parent)throws Exception {// (1) 根据名称查找特定的配置String value CatalinaProperties.getProperty(name .loader);if ((value null) || (value.equals()))return parent;value replace(value);ListRepository repositories new ArrayListRepository();StringTokenizer tokenizer new StringTokenizer(value, ,);while (tokenizer.hasMoreElements()) {String repository tokenizer.nextToken().trim();if (repository.length() 0) {continue;}// Check for a JAR URL repositorytry {SuppressWarnings(unused)URL url new URL(repository);repositories.add(new Repository(repository, RepositoryType.URL));continue;} catch (MalformedURLException e) {// Ignore}// Local repositoryif (repository.endsWith(*.jar)) {repository repository.substring(0, repository.length() - *.jar.length());repositories.add(new Repository(repository, RepositoryType.GLOB));} else if (repository.endsWith(.jar)) {repositories.add(new Repository(repository, RepositoryType.JAR));} else {repositories.add(new Repository(repository, RepositoryType.DIR));}}// (2) 类加载器工厂创建特定类加载器return ClassLoaderFactory.createClassLoader(repositories, parent);}代码清单中(1)处注释是根据上图中传递的“名称”加上后缀.loader去某个配置文件加载文件为了突出重点这里直接给出结论其加载的内容为/org/apache/catalina/startup/catalina.properties比如要加载 common.loader对应的value其在文件中的值为${catalina.base}/lib,${catalina.base}/lib/*.jar,${catalina.home}/lib,${catalina.home}/lib/*.jar也就是说Common ClassLoader要加载的路径是这些是Tomcat运行要使用的公共组件比如servlet-api.jar、catalina.jar等而我们发现当要加载server.loader和shared.loader时其key在配置文件中的value为空也就是说默认情况下Catalina ClassLoader和Shared ClassLoader(Tomcat整体类加载体系结构图中红色虚线内)都不存在只有Common ClassLoader 方法中的第二个参数表示创建类加载器的父类加载器是哪个再看initClassLoaders()方法图中代码在创建catalinaLoader和sharedLoader时父类加载器传入的实际上就是commonLoader以此可以验证图1中Catalina ClassLoader、Shared ClassLoader和Common ClassLoader的父子关系。而common ClassLoader的父类加载器参数传递的为null为什么null就会导致该类加载器的父类加载器为System ClassLoader呢我们需要进入代码清单2中看注释(2)处标识的代码 代码清单3 public static ClassLoader createClassLoader(ListRepository repositories,final ClassLoader parent)throws Exception {if (log.isDebugEnabled())log.debug(Creating new class loader);// Construct the class path for this class loaderSetURL set new LinkedHashSetURL();// 加载指定路径下的资源对象if (repositories ! null) {for (Repository repository : repositories) {if (repository.getType() RepositoryType.URL) {URL url buildClassLoaderUrl(repository.getLocation());if (log.isDebugEnabled())log.debug( Including URL url);set.add(url);} else if (repository.getType() RepositoryType.DIR) {File directory new File(repository.getLocation());directory directory.getCanonicalFile();if (!validateFile(directory, RepositoryType.DIR)) {continue;}URL url buildClassLoaderUrl(directory);if (log.isDebugEnabled())log.debug( Including directory url);set.add(url);} else if (repository.getType() RepositoryType.JAR) {File filenew File(repository.getLocation());file file.getCanonicalFile();if (!validateFile(file, RepositoryType.JAR)) {continue;}URL url buildClassLoaderUrl(file);if (log.isDebugEnabled())log.debug( Including jar file url);set.add(url);} else if (repository.getType() RepositoryType.GLOB) {File directorynew File(repository.getLocation());directory directory.getCanonicalFile();if (!validateFile(directory, RepositoryType.GLOB)) {continue;}if (log.isDebugEnabled())log.debug( Including directory glob directory.getAbsolutePath());String filenames[] directory.list();if (filenames null) {continue;}for (int j 0; j filenames.length; j) {String filename filenames[j].toLowerCase(Locale.ENGLISH);if (!filename.endsWith(.jar))continue;File file new File(directory, filenames[j]);file file.getCanonicalFile();if (!validateFile(file, RepositoryType.JAR)) {continue;}if (log.isDebugEnabled())log.debug( Including glob jar file file.getAbsolutePath());URL url buildClassLoaderUrl(file);set.add(url);}}}}// Construct the class loader itselffinal 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 PrivilegedActionURLClassLoader() {Overridepublic URLClassLoader run() {if (parent null)return new URLClassLoader(array);elsereturn new URLClassLoader(array, parent);}});}大块的if中的代码实际上是对资源进行转化加载的过程而return部分才是返回类加载器的部分代码根据是否有parent调用了URLClassLoader不同的构造器Common ClassLoader调用的是没有parent的构造器 图4. Common ClassLoader的parent创建的底层关键代码 按红线所画Common ClassLoader的parent实际上是JDK中sun.misc.Launcher.class类的loader成员变量而在上一篇文章中已经知道该loader的值就是应用类加载器系统类加载器System ClassLoader。至此Tomcat中类加载机制和JDK的类加载机制也建立上了联系 现在Tomcat的类加载机制已完成了一大半剩下用于加载每个web应用的类加载器WebApp ClassLoader的分析这个时候需要重新回到代码清单1中看注释(2)以下的部分其主要做的事情是通过反射创建了org.apache.catalina.startup.Catalina类的实例然后调用了签名为void setParentClassLoader(ClassLoader parentClassLoader)的方法并传入了Shared ClassLoader上面我们说过默认情况下Shared ClassLoader就是Common ClassLoader因此其传入的参数实际上是Common ClassLoader 我们思考既然有保存parent的方法必定使用时会调用获得parent方法那么我们需要查看Catalina类中ClassLoader getParentClassLoader()方法的调用栈层级关系比较复杂要紧跟主线不要迷失最终定位到StandardContext中的synchronized void startInternal() throws LifecycleException方法其中涉及到Tomcat的各种组件关系生命周期管理等内容将在下次分析Tomcat组件文章中详细介绍下面是只保留核心逻辑的startInternal()方法 代码清单4 protected synchronized void startInternal() throws LifecycleException {// 其他逻辑略......// Add missing components as necessaryif (webappResources null) { // (1) Required by Loaderif (log.isDebugEnabled())log.debug(Configuring default Resources);try {String docBase getDocBase();if (docBase null) {setResources(new EmptyDirContext());} else if (docBase.endsWith(.war) !(new File(getBasePath())).isDirectory()) {setResources(new WARDirContext());} else {setResources(new FileDirContext());}} catch (IllegalArgumentException e) {log.error(sm.getString(standardContext.resourcesInit), e);ok false;}}if (ok) {if (!resourcesStart()) {throw new LifecycleException(Error in resourceStart());}}// (1) 为每一个web应用创建一个WebappLoaderif (getLoader() null) {WebappLoader webappLoader new WebappLoader(getParentClassLoader());webappLoader.setDelegate(getDelegate());setLoader(webappLoader);}// 其他逻辑略......try {if (ok) {// (2) 调用WebappLoader的start// Start our subordinate components, if anyif ((loader ! null) (loader instanceof Lifecycle))((Lifecycle) loader).start();}// 其他逻辑省略......} finally {// Unbinding threadunbindThread(oldCCL);}}(1)处注释下的代码逻辑就是为每一个web应用创建一个类加载器该类加载器的父类加载器就是通过getParentClassLoader()得到的Shared ClassLoader(Common ClassLoader)(2)处代码调用了WebappLoader的start方法继续跟进 protected void startInternal() throws LifecycleException {// 其他逻辑省略.....try {//创建类加载器关键方法classLoader createClassLoader();classLoader.setResources(container.getResources());classLoader.setDelegate(this.delegate);classLoader.setSearchExternalFirst(searchExternalFirst);if (container instanceof StandardContext) {classLoader.setAntiJARLocking(((StandardContext) container).getAntiJARLocking());classLoader.setClearReferencesRmiTargets(((StandardContext) container).getClearReferencesRmiTargets());classLoader.setClearReferencesStatic(((StandardContext) container).getClearReferencesStatic());classLoader.setClearReferencesStopThreads(((StandardContext) container).getClearReferencesStopThreads());classLoader.setClearReferencesStopTimerThreads(((StandardContext) container).getClearReferencesStopTimerThreads());classLoader.setClearReferencesHttpClientKeepAliveThread(((StandardContext) container).getClearReferencesHttpClientKeepAliveThread());}// 其他逻辑省略.....}由于Tomcat的设计WebappLoader的start方法实际上调用的是父类的模板而模板中的startinternal方法由各个子类具体实现其中最关键的方法为createClassLoader() 图5. WebappLoader中createClassLoader方法 上图中的loadClass成员变量的值为org.apache.catalina.loader.WebappClassLoader所以实际上该类为每一个web应用创建了一个WebappClassLoader的实例该实例的parent就是Shared ClassLoader或者Common ClassLoader至此WebApp ClassLoader在图1中的位置也得以验证。 从理论上分析来看由于类加载的“双亲委派”机制一个类加载器只能加载本加载器指定的目录以及使用有“继承”关系的父类加载器加载过的类而Tomcat为每一个Web应用创建了一个WebappClassLoader不同的WebappClassLoader是同级关系不会存在交叉访问的问题从而达到web应用相互隔离的目的。 那Tomcat是否没有破坏双亲委派机制呢我们通过查看WebappClassLoader及其父类WebappClassLoaderBase的loadClass()和findClass()分析一下Tomcat加载web应用相关类的策略 public Class? loadClass(String name, boolean resolve) throws ClassNotFoundException {synchronized (getClassLoadingLockInternal(name)) {if (log.isDebugEnabled())log.debug(loadClass( name , resolve ));Class? clazz null;// Log access to stopped classloaderif (!started) {try {throw new IllegalStateException();} catch (IllegalStateException e) {log.info(sm.getString(webappClassLoader.stopped, name), e);}}// (1) // Check our previously loaded local class cacheclazz findLoadedClass0(name);if (clazz ! null) {if (log.isDebugEnabled())log.debug( Returning class from cache);if (resolve)resolveClass(clazz);return (clazz);}// (2)// Check our previously loaded class cacheclazz findLoadedClass(name);if (clazz ! null) {if (log.isDebugEnabled())log.debug( Returning class from cache);if (resolve)resolveClass(clazz);return (clazz);}// (3)// Try loading the class with the system class loader, to prevent// the webapp from overriding J2SE classestry {clazz j2seClassLoader.loadClass(name);if (clazz ! null) {if (resolve)resolveClass(clazz);return (clazz);}} catch (ClassNotFoundException e) {// Ignore}// Permission to access this class when using a SecurityManagerif (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;if (name.endsWith(BeanInfo)) {// BZ 57906: suppress logging for calls from// java.beans.Introspector.findExplicitBeanInfo()log.debug(error, se);} else {log.info(error, se);}throw new ClassNotFoundException(error, se);}}}// (4)boolean delegateLoad delegate || filter(name);// (5)// Delegate to our parent if requestedif (delegateLoad) {if (log.isDebugEnabled())log.debug( Delegating to parent classloader1 parent);try {clazz Class.forName(name, false, parent);if (clazz ! null) {if (log.isDebugEnabled())log.debug( Loading class from parent);if (resolve)resolveClass(clazz);return (clazz);}} catch (ClassNotFoundException e) {// Ignore}}// (6)// Search local repositoriesif (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}// Delegate to parent unconditionallyif (!delegateLoad) {if (log.isDebugEnabled())log.debug( Delegating to parent classloader at end: parent);try {clazz Class.forName(name, false, parent);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);}我们首先定位到WebappClassLoaderBase的loadClass方法(1)处首先看name对应的类是否存在缓存中缓存是一个ConcurrentHashMapString, ResourceEntry的集合如果没有缓存执行(2)处逻辑从JVM中查找是否曾今加载过该类(3)中的代码保证自定义类不会覆盖java基础类库中的类(4)的逻辑就是是否进行双亲委派的分叉口其中delegate默认为false那么就要看filter(String)方法该方法的内部实际上将待加载类的全路径名称和一个成员变量protected static final String[] packageTriggers中的类名进行比较如果待加载的类名和packageTriggers数组中的内容前缀匹配则需要委派父类加载即执行(5)处代码否则执行(6)调用重写的findClass(String)方法加载该类 public Class? findClass(String name) throws ClassNotFoundException {// 其他代码略去.....// Ask our superclass to locate this class, if possible// (throws ClassNotFoundException if it is not found)Class? clazz null;try {if (log.isTraceEnabled())log.trace( findClassInternal( name ));// (1)if (hasExternalRepositories searchExternalFirst) {try {clazz super.findClass(name);} catch(ClassNotFoundException cnfe) {// Ignore - will search internal repositories next} catch(AccessControlException ace) {log.warn(WebappClassLoaderBase.findClassInternal( name ) security exception: ace.getMessage(), ace);throw new ClassNotFoundException(name, ace);} catch (RuntimeException e) {if (log.isTraceEnabled())log.trace( --RuntimeException Rethrown, e);throw e;}}// (2)if ((clazz null)) {try {clazz findClassInternal(name);} catch(ClassNotFoundException cnfe) {if (!hasExternalRepositories || searchExternalFirst) {throw cnfe;}} catch(AccessControlException ace) {log.warn(WebappClassLoaderBase.findClassInternal( name ) security exception: ace.getMessage(), ace);throw new ClassNotFoundException(name, ace);} catch (RuntimeException e) {if (log.isTraceEnabled())log.trace( --RuntimeException Rethrown, e);throw e;}}//其他代码略去........return (clazz);} (1)处由于hasExternalRepositories和searchExternalFirst默认为false因此执行(2)处逻辑调用findClassInternal(String)方法 图6. WebappClassLoader类的findClassInternal方法 其主要的思想是根据待加载类的全路径读取该类的二进制数据进而进行类的预定义、class source的解析等将该类加载到JVM中 综上所述我认为Tomcat的类加载机制不能算完全“正统”的双亲委派WebappClassLoader内部重写了loadClass和findClass方法实现了绕过“双亲委派”直接加载web应用内部的资源当然可以通过在Context.xml文件中加上Loader delegate true开启正统的“双亲委派”加载机制
http://www.zqtcl.cn/news/681195/

相关文章:

  • 网站开发好学嘛网络安全工程师年薪
  • 17网站一起做网店睡衣网线制作流程
  • 广告网站设计公司好吗网站页面设计主要包括
  • 网站的做重庆市建设工程造价信息表
  • 建网站跟建网店的区别怎样营销建设网站
  • 医院做网站的风格乐清网站建设哪家好
  • 手机商城网站方案如何自己搭建微信小程序
  • 做影视免费网站违法吗青岛快速排名优化
  • 网站建设在电子商务中的作用的看法360地图怎么添加商户
  • 网站域名备案与不备案的区别wordpress 注册审核
  • 大学生做企业网站网页设计免费模板情侣
  • 商城网站建设教程网站开发支付宝
  • 广安网站设计快递加盟代理
  • 建设网站的建筑公司宿迁华夏建设集团网站
  • 百度推广网站建设费利用阿里云虚拟主机做网站
  • 吐槽做网站论坛模板
  • 广水住房和城乡建设部网站简单网页制作代码html
  • 建设网站找什么仿门户网站
  • 贵阳手机网站建设公司沈阳图书设计公司
  • 哪里做网站比较好在哪里注册域名
  • 做搜狗pc网站软件下载广告设计与制作学什么
  • 软件工程 旅游网站开发er图昆山网站建设网站建设
  • 网站下载的网页修改下面版权所有企业建设营销型网站的目的有
  • 官方重大项目建设库网站手机ps软件如何做ppt下载网站
  • 全国加盟网站大全海尔网站建设目标
  • wordpress 企业站模版自己做视频网站可以吗
  • 建设电子商务网站的方法有广东网站开发收费
  • php网站页面转wordpress网站广告代码
  • 在线网站建设教程网站版面布局结构
  • 网站建设提议网站建设怎么在图片上加字