美文网首页
每个ClassLoader一个单例

每个ClassLoader一个单例

作者: ThomasYoungK | 来源:发表于2018-06-12 07:27 被阅读18次

    在写单例代码的时候,一直以为单例就只有1个实例,这次看到了一个例外,就是用不同的ClassLoader创建的实例会不同。原因是:一个类,由不同的类加载器实例加载的话,会在方法区产生两个不同的类,彼此不可见,并且在堆中生成不同Class实例。因此单例也是相互隔离的,话不多说,上代码:
    单例类Singleton

    package cn.javass.spring.chapter3.bean;
    
    public class Singleton {
       
        //1.私有化构造器
        private Singleton() {}
        
        //2.单例缓存者,惰性初始化,第一次使用时初始化
        private static class InstanceHolder {
            private static final Singleton INSTANCE = new Singleton();
        }
        //3.提供全局访问点
        public static Singleton getInstance() {
            return InstanceHolder.INSTANCE;
        }
        
        //4.提供一个计数器来验证一个ClassLoader一个实例
        private int counter=0;
    }
    

    自定义类加载器SingletonClassLoader:

    package cn.javass.spring.chapter3;
    
    import java.io.IOException;
    import java.io.InputStream;
    
    import org.springframework.core.io.ClassPathResource;
    
    public class SingletonClassLoader extends ClassLoader {
        
        @Override
        public Class<?> loadClass(String name) throws ClassNotFoundException {
            //对于所有非cn.javass包下的类使用父类加载
            if(!name.startsWith("cn.javass")) {
                return super.loadClass(name);
            }
            try {
                //读取class文件资源
                InputStream is = new ClassPathResource(name.replace(".", "/") + ".class").getInputStream();
                int avaliable = is.available();
                byte[] bytes = new byte[avaliable];
                is.read(bytes, 0, avaliable);
                //定义类
                return defineClass(name, bytes, 0, avaliable);
            } catch (IOException e) {
                System.out.println(e);
                return super.loadClass(name);
            }
        }
    }
    

    测试用例SingletonTest:

    package cn.javass.spring.chapter3;
    
    /**
     * Created by thomas_young on 11/6/2018.
     */
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;
    
    
    import org.junit.Assert;
    import org.junit.Test;
    
    import cn.javass.spring.chapter2.HelloImpl2;
    
    
    
    public class SingletonTest {
    
        @Test
        public void testSingleton() throws Exception {
            //第一个单例
            //1.创建一个ClassLoader
            ClassLoader classLoader = new SingletonClassLoader();
            //2.加载需要的类
            Class clazz = classLoader.loadClass("cn.javass.spring.chapter3.bean.Singleton");
            //3.通过反射获取单例对象
            Method getInstance = clazz.getDeclaredMethod("getInstance");
            Object singletonObj = getInstance.invoke(clazz);
            System.out.println(singletonObj.toString());
            //4.通过反射获取字段counter值
            Field counterField = clazz.getDeclaredField("counter");
            counterField.setAccessible(true);
            Integer counter = (Integer) counterField.get(singletonObj);
            //5.对字段counter自增1
            counterField.set(singletonObj, counter + 1);
            //6.验证counter=1
            Assert.assertEquals(1, counterField.get(singletonObj));
            System.out.println(counterField.get(singletonObj));
    
            // 相同class创建的实例对象是一样的
            Object singletonObj1 = getInstance.invoke(clazz);
            System.out.println(singletonObj1.toString());
            //通过反射获取字段counter值
            Integer counter1 = (Integer) counterField.get(singletonObj1);
            //对字段counter自增1
            counterField.set(singletonObj1, counter1 + 1);
            //验证counter=2
            Assert.assertEquals(2, counterField.get(singletonObj1));
            System.out.println(counterField.get(singletonObj1));
    
            //第二个单例
            //1.创建一个ClassLoader
            ClassLoader classLoader2 = new SingletonClassLoader();
            //2.加载需要的类
            Class clazz2 = classLoader2.loadClass("cn.javass.spring.chapter3.bean.Singleton");
            //3.通过反射获取单例对象
            Method getInstance2 = clazz2.getDeclaredMethod("getInstance");
            Object singletonObj2 = getInstance2.invoke(clazz2);
            System.out.println(singletonObj2.toString());
            //4.通过反射获取字段counter值
            Field counterField2 = clazz2.getDeclaredField("counter");
            //5.对字段counter自增1
            counterField2.setAccessible(true);
            Integer counter2 = (Integer) counterField2.get(singletonObj2);
            counterField2.set(singletonObj2, counter2 + 1);
            //6.验证counter=1
            Assert.assertEquals(1, counterField2.get(singletonObj2));
            System.out.println(counterField2.get(singletonObj2));
            //以上就证明了每个ClassLoader一个单例(这句话相当重要!!!)
        }
    

    测试输出:

    cn.javass.spring.chapter3.bean.Singleton@71dac704
    1
    cn.javass.spring.chapter3.bean.Singleton@71dac704
    2
    cn.javass.spring.chapter3.bean.Singleton@6f75e721
    1
    

    相关文章

      网友评论

          本文标题:每个ClassLoader一个单例

          本文链接:https://www.haomeiwen.com/subject/djakeftx.html