线程上下文类加载器的作用

在spring中,有一个获取默认的类加载器的方法

public static ClassLoader getDefaultClassLoader() {
		ClassLoader cl = null;
		try {
			cl = Thread.currentThread().getContextClassLoader();
		}
		catch (Throwable ex) {
			// Cannot access thread context ClassLoader - falling back...
		}
		if (cl == null) {
			// No thread context class loader -> use class loader of this class.
			cl = ClassUtils.class.getClassLoader();
			if (cl == null) {
				// getClassLoader() returning null indicates the bootstrap ClassLoader
				try {
					cl = ClassLoader.getSystemClassLoader();
				}
				catch (Throwable ex) {
					// Cannot access system ClassLoader - oh well, maybe the caller can live with null...
				}
			}
		}
		return cl;
	}

获取的是线程上下文加载器,其实这个加载器默认的情况就是系统加载器,跟这个ClassLoader.getSystemClassLoader()一样的,那为什么要这么写呢?

之前在tomcat中部署项目,可以部署很多个war包,其中war中的类很多类路径+类名都是一样,根据双亲委派,一个com.fen.dou.TestCl类只能被加载一次,然而会造成版本冲突,tomcat是怎么解决的呢?

在java程序中是由Launcher来进行加载启动程序的

    public Launcher() {
        Launcher.ExtClassLoader var1;
        try {
            var1 = Launcher.ExtClassLoader.getExtClassLoader();
        } catch (IOException var10) {
            throw new InternalError("Could not create extension class loader", var10);
        }

        try {
            this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
        } catch (IOException var9) {
            throw new InternalError("Could not create application class loader", var9);
        }

        Thread.currentThread().setContextClassLoader(this.loader);
        String var2 = System.getProperty("java.security.manager");
        if (var2 != null) {
            SecurityManager var3 = null;
            if (!"".equals(var2) && !"default".equals(var2)) {
                try {
                    var3 = (SecurityManager)this.loader.loadClass(var2).newInstance();
                } catch (IllegalAccessException var5) {
                } catch (InstantiationException var6) {
                } catch (ClassNotFoundException var7) {
                } catch (ClassCastException var8) {
                }
            } else {
                var3 = new SecurityManager();
            }

            if (var3 == null) {
                throw new InternalError("Could not create SecurityManager: " + var2);
            }

            System.setSecurityManager(var3);
        }

    }

里面初始化了一个系统加载器  this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);

这个加载器,可以通过ClassLoader.getSystemClassLoader()获取,整个java程序就只会有一个系统加载器,符合双亲委派,所以默认整个java程序不会出现重复加载相同类的情况

我们可以自定义两个ClassLoader,打破双亲委派,重写findClass,分别加载不同的spring项目

package com.fen.dou;

import lombok.SneakyThrows;
import org.springframework.core.io.UrlResource;
import org.springframework.core.io.support.PropertiesLoaderUtils;
import org.springframework.util.StringUtils;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.Enumeration;
import java.util.Map;
import java.util.Properties;

public class MyClassLoaderTest implements Runnable{

    private static ClassLoader commoncl = null;
    private static ClassLoader customcl = null;

    public MyClassLoaderTest() {

    }
    public static ClassLoader getCommonClassLoader() throws IOException {
        if (commoncl == null) {
            synchronized(MyClassLoaderTest.class) {
                if (commoncl == null) {
                    commoncl = new MyClassLoader("F:\\code\\nacosServiceProvider\\provider3303\\target\\classes");
                }
            }
        }

        return commoncl;
    }
    public static ClassLoader getCustomClassLoader() throws IOException {
        if (customcl == null) {
            synchronized(MyClassLoaderTest.class) {
                if (customcl == null) {
                    customcl = new MyClassLoader("F:\\code\\nacosServiceProvider\\proviver3302\\target\\classes");
                }
            }
        }

        return customcl;
    }
    @SneakyThrows
    @Override
    public void run() {
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        try {
            Thread.currentThread().setContextClassLoader(getCommonClassLoader());
            Class clazz = Thread.currentThread().getContextClassLoader().loadClass("com.fen.dou.TestCl");
            Object obj = clazz.newInstance();
            Method method= clazz.getDeclaredMethod("say", null);
            method.invoke(obj, null);
          

            Thread.currentThread().setContextClassLoader(getCustomClassLoader());
            Class clazz1 = Thread.currentThread().getContextClassLoader().loadClass("com.fen.dou.TestCl");
            Object obj1 = clazz1.newInstance();
            Method method1= clazz1.getDeclaredMethod("say", null);
            method1.invoke(obj1, null);


     //       print(Thread.currentThread().getContextClassLoader(),"application.yml");
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            Thread.currentThread().setContextClassLoader(classLoader);
          //  print(classLoader,"META-INF/spring.factories");
        }
    }

   void  print(ClassLoader classLoader,String path) throws IOException {
        Enumeration<URL> urls = (classLoader != null ?
                classLoader.getResources(path) :
                ClassLoader.getSystemResources(path));
        while (urls.hasMoreElements()) {
            URL url = urls.nextElement();
            UrlResource resource = new UrlResource(url);
            Properties properties = PropertiesLoaderUtils.loadProperties(resource);
            for (Map.Entry<?, ?> entry : properties.entrySet()) {
                String factoryClassName = ((String) entry.getKey()).trim();
                for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
                    //       System.out.println("---factoryClassName--"+factoryClassName);
                    System.out.println("---"+classLoader.getClass().getName()+"--"+factoryName.trim());
                }
            }
        }
    }
    static class MyClassLoader extends ClassLoader {
        private String classPath;

        public MyClassLoader(String classPath) {
            this.classPath = classPath;
        }

        private byte[] loadByte(String name) throws Exception {
            name = name.replaceAll("\\.", "/");
            FileInputStream fis = new FileInputStream(classPath + "/" + name + ".class");
            int len = fis.available();
            byte[] data = new byte[len];
            fis.read(data);
            fis.close();
            return data;
        }

        protected Class<?> findClass(String name) throws ClassNotFoundException {
            try {
                byte[] data = loadByte(name);
                //defineClass将一个字节数组转为Class对象,这个字节数组是class文件读取后最终 的字节数组。
                return defineClass(name, data, 0, data.length);
            } catch (Exception e) {
                e.printStackTrace();
                throw new ClassNotFoundException();
            }
        }

    }

    public static void main(String args[]) throws Exception {

        MyClassLoaderTest classLoader = new MyClassLoaderTest();
        new Thread(classLoader).start();

    }
}

现在还没说明spring要用线程上下文加载器

main线程    刚开始使用的是 AppClassLoader,我现在要使用自定义打破双亲委派的 CommonClassLoader 加载器 去加载 spring 项目,是不是需要切换 加载器,由AppClassLoader切换成 CommonClassLoader,那怎么切换呢?

        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        try {
            Thread.currentThread().setContextClassLoader(getCommonClassLoader());
            Class clazz = Thread.currentThread().getContextClassLoader().loadClass("com.fen.dou.TestCl");
            Object obj = clazz.newInstance();
            Method method= clazz.getDeclaredMethod("say", null);
            method.invoke(obj, null);
         
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            Thread.currentThread().setContextClassLoader(classLoader);
  
        }

则tomcat可以用 AppClassLoader加载自身的类,用Thread.currentThread().setContextClassLoader(getCommonClassLoader());方式切换加载器,加载spring项目的类,那spring项目通过 Thread.currentThread().getContextClassLoader();来进行接收这个加载器

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值