在写单例代码的时候,一直以为单例就只有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
网友评论