
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