我们定义主要类的角色:
- SpringClassLoader:当该类被设定为主线程默认的类加载器后,会进行之后全部类的加载,也会进行类加载的调度工作(如果父类加载器(AppClassLoader)没有加载到该类 则交给ServiceClassLoader 进行加载)
- ServiceClassLoader:进行制定路径下类的加载工作并与SpringClassLoader进行lib库环境的隔离(SpringClassLoader持有ServiceClassLoader的引用,并非完全意义的隔离,只是lib 环境的隔离)
- ServiceContainer: ClassLoader装配和初始化工作, 并设置当前环境默认的类加载器为SpringClassLoader类加载器
直接上代码:
ServiceClassLoader
package com.jvm.separator;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Enumeration;
import java.util.concurrent.ConcurrentHashMap;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
/**
* Created with IntelliJ IDEA.
* Description:
* User: zhubo
* Date: 2018-03-24
* Time: 16:14
*/
public class ServiceClassLoader extends URLClassLoader {
private static ConcurrentHashMap<String,Class<?>> classCacheMap =
new ConcurrentHashMap<String, Class<?>>();
private URL[] urls;
public ServiceClassLoader(URL[] urls, ClassLoader parent) {
super(urls, parent);
this.urls = urls;
init();
}
private void init(){
for(URL url : urls){
initClassName(url);
}
}
private void initClassName(URL url){
String path = url.getPath();
try{
JarFile jarFile = new JarFile(path);
Enumeration<JarEntry> entrys = jarFile.entries();
while(entrys.hasMoreElements()){
JarEntry jarEntry = entrys.nextElement();
String classFileName = jarEntry.getName();
if(classFileName.endsWith(".class")){
classFileName = classFileName.replace("/",".");
String className = classFileName.substring(0,classFileName.lastIndexOf("."));
loadClass(className);
}
}
}catch (Exception e){
e.printStackTrace();
}
}
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
Class<?> clazz = classCacheMap.get(name);
if(clazz == null){
clazz = super.loadClass(name);
classCacheMap.put(name,clazz);
}
return clazz;
}
}
SpringClassLoader
package com.jvm.separator;
/**
* Created with IntelliJ IDEA.
* Description: 设置为当前上下文默认类加载器 , 保证顺序加载
* User: zhubo
* Date: 2018-03-24
* Time: 16:13
*/
public class SpringClassLoader extends ClassLoader {
//策略模式
private ServiceClassLoader serviceClassLoader;
public ServiceClassLoader getServiceClassLoader() {
return serviceClassLoader;
}
public void setServiceClassLoader(ServiceClassLoader serviceClassLoader) {
this.serviceClassLoader = serviceClassLoader;
}
/**
* @param name
* @return
*/
private Class<?> loadClasspathClass(String name){
try{
return super.loadClass(name);
}catch (Exception e){
//下面打印要注释,因为classpath 的ClassLoader处于处于类加载的中间阶段 ,
// 接下来还有自定义类加载器进行类的加载 如果还没有加载到就抛出异常
//e.printStackTrace();
}
return null;
}
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
try{
Class<?> clazz = loadClasspathClass(name);
if(clazz == null){
clazz = loadThridLibsClass(name);
}
if(clazz != null){
return clazz;
}
}catch (Exception e){
e.printStackTrace();
}
throw new ClassNotFoundException(name);
}
private Class<?> loadThridLibsClass(String name){
try{
return serviceClassLoader.loadClass(name);
}catch (Exception e){
}
return null;
}
}
ServiceContainer
package com.jvm.separator;
import java.io.File;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
/**
* Created with IntelliJ IDEA.
* Description: 服务容器,用于装载,启动目录服务
* User: zhubo
* Date: 2018-03-24
* Time: 14:52
*/
public class ServiceContainer {
//ServiceClassLoader 默认加载路径
private static final String DEFAULT_SERVICE_PATH = "META-INF/service";
//策略模式
private static SpringClassLoader springClassLoader;
/**
* ClassLoader装配和初始化工作, SpringClassLoader 和 ServiceClassLoader进行隔离,
* 如果SpringClassLoader加载不到的类 到 ServiceClassLoader中进行加载,这样既保证了两个“类库”隔离的效果,保证了 两个 “类库” 中类的相对加载顺序
*/
public static void start(){
start(DEFAULT_SERVICE_PATH);
}
public static void start (String serviceDir) {
ClassLoader parent = ServiceContainer.class.getClassLoader();
ServiceClassLoader serviceClassLoader = new ServiceClassLoader(parseServiceDir(serviceDir), parent);
springClassLoader = new SpringClassLoader();
springClassLoader.setServiceClassLoader(serviceClassLoader);
//设置当前环境默认的类加载器为SpringClassLoader类加载器 (这个类确保两个隔离环境 类的加载顺序)
Thread.currentThread().setContextClassLoader(springClassLoader);
}
//工厂模式
private static URL[] parseServiceDir(String serviceDir) {
try {
URL url = ServiceContainer.class.getClassLoader().getResource(serviceDir);
if (url != null) {
serviceDir = url.getPath();
}
List<java.net.URL> urlList = new ArrayList<java.net.URL>();
File dir = new File(serviceDir);
if (!dir.isDirectory()) {
throw new IllegalStateException(dir.getPath() + " is not dir");
}
for (File file : dir.listFiles()) {
if (file.getPath().endsWith(".jar")) {
urlList.add(new URL("file:" + file.getPath()));
}
}
if (urlList.isEmpty()) {
throw new IllegalStateException("urlList is empty");
}
return urlList.toArray(new java.net.URL[0]);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static Class<?> loadClass(String name) throws ClassNotFoundException {
return springClassLoader.loadClass(name);
}
}
MainExec
package com.jvm.separator;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.lang.reflect.Method;
/**
* Created with IntelliJ IDEA.
* Description: 入口主函数
* User: zhubo
* Date: 2018-03-24
* Time: 14:47
*/
public class MainExec {
public static void main(String[] args) throws Exception{
String startClass = "org.springframework.context.support.ClassPathXmlApplicationContext";
// 任意绝对路径
//ServiceContainer.start("C:/services");
ServiceContainer.start();
Class<?> aClass = ServiceContainer.loadClass(startClass);
ClassPathXmlApplicationContext ctx =
(ClassPathXmlApplicationContext) aClass.getConstructor(new Class<?>[]{String.class}).newInstance(new Object[]{"beans.xml"});
Object obj = ctx.getBean("helloService");
System.out.println(obj);
Method method = obj.getClass().getDeclaredMethod("sayHello", new Class[]{String.class});
method.invoke(obj,"hello");
}
}
输出结果
foo.HelloServiceImpl@6325a3ee
hello world