每个ClassLoader一个单例

2018-06-12  本文已影响18人  ThomasYoungK

在写单例代码的时候,一直以为单例就只有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
上一篇下一篇

猜你喜欢

热点阅读