解决循环依赖问题思路是使用二级缓存,创建了实例后先放入earlySingletonObjects中,加载所有属性成为成功实例后再放入singletonObjects中并从earlySingletonObjects中删除:
public static Map earlySingletonObjects = new HashMap<>();//单例对象,就存放半成品类型public static Map singletonObjects = new HashMap<>();//存放成品类型
其实使用一级缓存也可以搞定,但是可是放到多线程的环境中,可能就会出现空指针问题。 线程1刚把半成品a放入到缓存中,还未来得及将b注入进去。此时线程2直接在缓存中获取到了a,在尝试调用其所依赖的b的任何方法时,就会出现空指针异常。
代码如下:
public class Main3 {
public static Map earlySingletonObjects = new HashMap<>();//存放成品类型
public static Map singletonObjects = new HashMap<>();//单例对象,就存放半成品类型
private static Object getSingleton(String className)
{
Object singletonObject = singletonObjects.get(className);
if(singletonObject==null)
{
singletonObject = earlySingletonObjects.get(className);
}
return singletonObject;
}
private static <T> T getBean(Class<T> clazz) throws IllegalAccessException, InstantiationException {
//优先从缓存中获取
Object object = getSingleton(clazz.getName());//
if(object!=null)
{
return (T)object;
}
synchronized (singletonObjects)
{
object = singletonObjects.get(clazz.getName());
if(object!=null)
{
return (T)object;
}
//实例化对象
T instance = clazz.newInstance();
earlySingletonObjects.put(clazz.getName(),instance);//实例化后就放入缓存
//获取当前类中的所有字段
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
//允许访问私有变量
field.setAccessible(true);
//判断字段是否被@Load注解修饰
boolean isUseLoad = field.isAnnotationPresent(Load.class);
if (!isUseLoad) {
continue;
}
//获取需要被注入的字段的class
Class<?> fieldType = field.getType();
//递归获取字段的实例对象
Object fieldBean = getBean(fieldType);
//将实例对象注入到该字段中
field.set(instance, fieldBean);
}
earlySingletonObjects.remove(clazz.getName());
singletonObjects.put(clazz.getName(),instance);
return instance;
}
}
public static void main(String[] args) throws InstantiationException, IllegalAccessException {
/* A a = getBean(A.class);
System.out.println(a.getB().getClass());*/
new Thread(() -> {
try {
A a1 = getBean(A.class);
System.out.println("t1.a:" + a1.hashCode());
System.out.println("t1.b:" + a1.getB().hashCode());
} catch (IllegalAccessException | InstantiationException e) {
e.printStackTrace();
}
}).start();
new Thread(() -> {
try {
A a1 = getBean(A.class);
System.out.println("t2.a:" + a1.hashCode());
System.out.println("t2.b:" + a1.getB().hashCode());
} catch (IllegalAccessException | InstantiationException e) {
e.printStackTrace();
}
}).start();
new Thread(() -> {
try {
B b = getBean(B.class);
System.out.println("t3.b:" + b.hashCode());
System.out.println("t3.a:" + b.getA().hashCode());
} catch (IllegalAccessException | InstantiationException e) {
e.printStackTrace();
}
}).start();
}
}
public class A {
@Load
private B b;
public B getB() {
return b;
}
}
public class B {
@Load
private A a;
public A getA() {
return a;
}
}