在看下面的实现之前,可以先了解下面的几篇文章内容:
1 https://blog.csdn.net/u013412772/article/details/80837735
2 https://blog.csdn.net/u013412772/article/details/80842636
3 https://blog.csdn.net/u013412772/article/details/80851700
4 https://blog.csdn.net/u013412772/article/details/80865875
5 https://blog.csdn.net/u013412772/article/details/80906997
模块隔离功能:每个模块使用一个类加载器进行加载,每个模块独立作为一个Jar包对外提供功能.我们把每个模块的Jar里面的类加载到一个内存map,然后再创建一个自定义类加载器,重写loadClass方法,当加载类时候优先在内存map里面查找,如果存在则直接返回,否则调用默认加载方法.工程结构图如下:
将demo_classloader工程使用maven的命令clean package
命令打包成jar包(demo_classloader的pom.xml设置打包形式也是jar),如下所示:
在target目录下找到打成jar的文件:
其中datamanager工程里面的classloader包里面的ModuleManager是核心类,实现了从不同模块加载jar里面的类到内存map的功能,核心代码如下:
import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import com.alibaba.druid.util.StringUtils;
public class ModuleManager {
/**
* 用来并发加载多个模块jar
*/
private ExecutorService executorService = Executors.newFixedThreadPool(8);
/**
* cache用来存放所有Jar里面的Class对象
*/
@SuppressWarnings("rawtypes")
private Map<String, Class> cache = new ConcurrentHashMap<String, Class>();
/**
* moduleList是需要加载的模块的Jar的路径
*/
private List<String> moduleList = new ArrayList<String>();
/**
* 核心方法:其中future的call方法针对每个模块新建了一个URLClassLoader加载器用来加载该模块的类,并把加载的类放入内存map.
*/
public void init() {
System.out.println("----begin load all module----");
List<Future<String>> futureList = new ArrayList<Future<String>>();
for (String module : moduleList) {
Future<String> future = executorService.submit(new Callable<String>() {
@Override
public String call() throws Exception {
try {
// URL[] moduleURL = new URL[] { new URL("file://"
// +module) };
File moduleFile = new File(module);
URL moduleURL = moduleFile.toURI().toURL();
URL[] moduleUrl = new URL[] { moduleURL };
@SuppressWarnings("resource")
URLClassLoader classLoader = new URLClassLoader(moduleUrl);
@SuppressWarnings("resource")
JarFile jar = new JarFile(new File(module));
Enumeration<JarEntry> entries = jar.entries();
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
String className = getClassName(entry);
if (StringUtils.isEmpty(className)) {
continue;
}
try {
Class<?> clazz = classLoader.loadClass(className);
cache.put(className, clazz);
} catch (Exception e) {
e.printStackTrace();
}
}
} catch (Exception e) {
e.printStackTrace();
}
return module;
}
});
futureList.add(future);
}
for (Future<String> future : futureList) {
try {
String moduleName = future.get();
System.out.println("----load module " + moduleName + "ok");
} catch (Exception e) {
e.printStackTrace();
}
}
executorService.shutdown();
System.out.println("----end load all module----");
}
private String getClassName(JarEntry jarEntry) {
String jarName = jarEntry.getName();
if (!jarName.endsWith(".class")) {
return null;
}
if (jarName.charAt(0) == '/') {
jarName = jarName.substring(1);
}
jarName = jarName.replace("/", ".");
return jarName.substring(0, jarName.length() - 6);
}
public ExecutorService getExecutorService() {
return executorService;
}
public void setExecutorService(ExecutorService executorService) {
this.executorService = executorService;
}
public Map<String, Class> getCache() {
return cache;
}
public void setCache(Map<String, Class> cache) {
this.cache = cache;
}
public List<String> getModuleList() {
return moduleList;
}
public void setModuleList(List<String> moduleList) {
this.moduleList = moduleList;
}
}
ModuleClassLoader是自定义的一个类加载器,代码如下:
import java.net.URL;
import java.net.URLClassLoader;
public class ModuleClassLoader extends URLClassLoader {
private ModuleManager manager = new ModuleManager();
public ModuleClassLoader(URL[] urls, ModuleManager manager) {
super(urls);
this.manager = manager;
}
/**
* 重写了loadClass,优先从内存map查找要加载的类
*/
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
if (manager.getCache().containsKey(name)) {
return manager.getCache().get(name);
} else {
return super.loadClass(name, resolve);
}
}
}
ModuleClassLoaderTest是个测试类,如下所示:
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;
import classloader.ModuleClassLoader;
import classloader.ModuleManager;
public class ModuleClassLoaderTest {
@SuppressWarnings("resource")
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException,
InvocationTargetException, InstantiationException {
List<String> jarList = new ArrayList<String>();
jarList.add("D:\\workspace\\demo_classloader\\target\\demo_classloader-0.0.1-SNAPSHOT.jar");
ModuleManager manager = new ModuleManager();
manager.setModuleList(jarList);
manager.init();
ModuleClassLoader classLoader = new ModuleClassLoader(((URLClassLoader) ModuleClassLoaderTest.class.getClassLoader()).getURLs(), manager);
Class<?> moduleA = classLoader.loadClass("module.ModuleService");
Method sayHelloModuleA = moduleA.getMethod("sayHelloModule");
String result = (String) sayHelloModuleA.invoke(moduleA.newInstance(), null);
System.out.println(result);
}
}
首先创建了一个ModuleManager对象,设置需要加载模块的jar列表,然后调用init方法使用不同的类加载器加载不同jar里面的class到内存map.然后创建了一个ModuleClassLoader的实例,其内部维护这内存map,当使用classLoader.loadClass("module.ModuleService");
加载ModuleService
时,回去内存的map查看是否存在该类,如果有,则返回ModuleService
的class对象,然后创建一个ModuleService
的实例,最后通过反射调用了sayHelloModule
方法.