ClassLoader隔离机制的应用

GitHub

我们定义主要类的角色:

  1. SpringClassLoader:当该类被设定为主线程默认的类加载器后,会进行之后全部类的加载,也会进行类加载的调度工作(如果父类加载器(AppClassLoader)没有加载到该类 则交给ServiceClassLoader 进行加载)
  2. ServiceClassLoader:进行制定路径下类的加载工作并与SpringClassLoader进行lib库环境的隔离(SpringClassLoader持有ServiceClassLoader的引用,并非完全意义的隔离,只是lib 环境的隔离)
  3. 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

 

转载于:https://my.oschina.net/LucasZhu/blog/1674952

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值