All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.jianggujin.modulelink.util.JModuleClassLoader Maven / Gradle / Ivy

/**
 * Copyright 2018 jianggujin (www.jianggujin.com).
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.jianggujin.modulelink.util;

import java.io.IOException;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import com.jianggujin.modulelink.util.JLogFactory.JLog;

/**
 * 模块的ClassLoader,继承自URLClassLoader,同时可以强制指定一些包下的class,由本ClassLoader自己加载,
 * 不通过父ClassLoader加载,突破双亲委派机制。
 *
 */
public class JModuleClassLoader extends URLClassLoader {

   private static final JLog logger = JLogFactory.getLog(JModuleClassLoader.class);
   private final ConcurrentHashMap parallelLockMap;

   /**
    * java的包必须排除,避免安全隐患
    */
   public static final String[] DEFAULT_EXCLUDED_PACKAGES = new String[] { "java.", "javax.", "sun.", "oracle." };

   /**
    * 需要排除的包
    */
   private final Set excludedPackages = new HashSet();

   /**
    * 需要子加载器优先加载的包
    */
   private final Set overridePackages;

   private static boolean canCloseJar = false;
   private final List cachedJarFiles;

   static {
      // 1.7之后可以直接调用close方法关闭打开的jar,需要判断当前运行的环境是否支持close方法,如果不支持,需要缓存,避免卸载模块后无法删除jar
      try {
         URLClassLoader.class.getMethod("close");
         canCloseJar = true;
         if (logger.isDebugEnabled()) {
            logger.debug("use URLClassLoader#close()");
         }
      } catch (NoSuchMethodException e) {
      } catch (SecurityException e) {
      }
   }

   @SuppressWarnings("unchecked")
   public JModuleClassLoader(Set urls, ClassLoader parent, Set overridePackages) {
      super(new URL[] {}, parent);
      parallelLockMap = new ConcurrentHashMap();
      cachedJarFiles = canCloseJar ? null : new ArrayList();
      if (urls != null) {
         for (URL url : urls) {
            this.addURL(url);
         }
      }
      this.overridePackages = overridePackages == null ? Collections.EMPTY_SET : overridePackages;
      this.excludedPackages.addAll(new HashSet(Arrays.asList(DEFAULT_EXCLUDED_PACKAGES)));
   }

   @Override
   protected void addURL(URL url) {
      if (!canCloseJar) {
         try {
            // 打开并缓存文件url连接
            URLConnection uc = url.openConnection();
            if (uc instanceof JarURLConnection) {
               uc.setUseCaches(true);
               ((JarURLConnection) uc).getManifest();
               cachedJarFiles.add((JarURLConnection) uc);
            }
         } catch (Exception e) {
            logger.error("Failed to cache JAR file:" + url.toExternalForm());
         }
      }
      super.addURL(url);
   }

   /**
    * 覆盖双亲委派机制
    *
    * @see ClassLoader#loadClass(String, boolean)
    */
   @Override
   protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException {
      Class result = null;
      synchronized (getClassLoadingLock(name)) {
         if (isEligibleForOverriding(name)) {
            if (logger.isDebugEnabled()) {
               logger.debug("Load class for overriding: " + name);
            }
            result = loadClassForOverriding(name, resolve);
         }
         if (result != null) {
            // 链接类
            if (resolve) {
               resolveClass(result);
            }
            return result;
         }
         // 使用默认类加载方式
         return super.loadClass(name, resolve);
      }
   }

   /**
    * 加载一个子模块覆盖的类
    *
    * @param name
    * @return
    * @throws ClassNotFoundException
    */
   private Class loadClassForOverriding(String name, boolean resolve) throws ClassNotFoundException {
      // 查找已加载的类
      Class result = findLoadedClass(name);
      if (result == null) {
         // 加载类
         try {
            result = findClass(name);
         } catch (ClassNotFoundException e) {
            // 当前子模块系统加载不到时尝试从父容器中加载
            if (logger.isDebugEnabled()) {
               logger.debug("class will load from parent context: " + name);
            }
            result = super.loadClass(name, resolve);
         }
      }
      return result;
   }

   /**
    * 判断该名字是否是需要覆盖的 class
    *
    * @param name
    * @return
    */
   private boolean isEligibleForOverriding(final String name) {
      JAssert.checkNotNull(name, "name must not be null");
      if (!isExcluded(name)) {
         for (String overridePackage : overridePackages) {
            if (name.startsWith(overridePackage)) {
               return true;
            }
         }
      }
      return false;
   }

   /**
    * 判断class是否排除
    *
    * @param className
    * @return
    */
   protected boolean isExcluded(String className) {
      JAssert.checkNotNull(className, "className must not be null");
      for (String packageName : this.excludedPackages) {
         if (className.startsWith(packageName)) {
            return true;
         }
      }
      return false;
   }

   public void close() throws IOException {
      if (canCloseJar) {
         try {
            super.close();
         } catch (IOException ioe) {
            logger.warn("Invoke super close method fail", ioe);
         }
      } else {
         for (JarURLConnection conn : cachedJarFiles) {
            conn.getJarFile().close();
         }
         cachedJarFiles.clear();
      }
   }

   @Override
   public String toString() {
      StringBuilder sb = new StringBuilder(this.getClass().getSimpleName());
      sb.append("\r\n  excludedPackages: ");
      sb.append(excludedPackages);
      sb.append("\r\n  overridePackages: ");
      sb.append(overridePackages);
      sb.append("\r\n");
      if (this.getParent() != null) {
         sb.append("----------> Parent Classloader:\r\n");
         sb.append(this.getParent().toString());
      }
      return (sb.toString());
   }

   protected Object getClassLoadingLock(String className) {
      Object lock = this;
      if (parallelLockMap != null) {
         Object newLock = new Object();
         lock = parallelLockMap.putIfAbsent(className, newLock);
         if (lock == null) {
            lock = newLock;
         }
      }
      return lock;
   }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy