【JavaEE】自主实现Spring核心IOC和AOP之实现循环注入、单例、发现循环依赖关系

1 篇文章 0 订阅
1 篇文章 0 订阅

这篇博文我们主要介绍,循环注入和单例问题的解决。对于我们不可能去更改的某些类,如果他们之间也存在循环注入的关系,我们也不可能去更改源代码,这时,我们只能去发现其依赖关系,并抛出,交由用户去解决。
首先,准备一个循环注入的例子:

public class A {
	private B b;
	
	public A() {
	}

	public void setB(B b) {
		this.b = b;
	}
	
	@Override
	public String toString() {
		return "(A类中有成员b) ";
	}
}
public class B {
	private C c;
	
	public B() {
	}
	
	public void setC(C c) {
		this.c = c;
	}
	
	@Override
	public String toString() {
		return "(B类中有成员c) ";
	}
}
}
public class C {
	private A a;
	
	public C() {
	}
	
	public void setA(A a) {
		this.a = a;
	}
	
	@Override
	public String toString() {
		return "(C类中有成员a) " + a;
	}
}

上述的例子可以看到,A类中存在B类型的成员,B类型中存在C类型的成员,C类型中存在A类型的成员。由此,形成了一个循环链。这时,我们在注入的时候,就会发生循环注入。
对于上述问题的解决,我们只需要增加一个判断条件,是否这个类完成过注入!
在上一篇博客【JavaEE】自主实现Spring核心IOC和AOP之实现类的注入中,我们已经明确,要先进行Component注解的类的扫描工作,并把它加入到map中,在getBean()的时候,再进行类中的成员和方法的初始化。
所以,我们要将这个判断条件加入到getBean()方法中(也就是在getBean()方法中进行判断是否已经完成过初始化)。
针对上述的例子来看,我们首先要对A、B、C类加上Component注解,然后对每个类各自的set方法加上Autowired注解。在执行scanBeanByPackage()方法的时候,已经对A、B、C类进行了初始化,但是,类中的成员还没有进行初始化。由于这三个类是循环注入关系,所以,我们只需在getBean()的时候对他们的成员进行set()就足够了。因为这里的set操作会进行循环,所以我们需要一个判断条件。在BeanDefinition类中增加一个private boolean inject成员,来检测是否这个类的成员和方法进行过注入。看代码:

public class BeanDefinition {
	private Class<?> klass;
	private Object object;
	private boolean inject;
	private boolean singleton;
	
	public BeanDefinition() {
		this.singleton = true;
		this.inject = false;
	}

BeanFactory类:

	@SuppressWarnings("unchecked")
	public static <T> T getBean(String klassName) {
		BeanDefinition beanDefinition = beanPool.get(klassName);
		if (beanDefinition == null) {
			System.out.println(klassName + "没有找到");
			return null;
		}
		
		Object object = beanDefinition.getObject();
		if (!beanDefinition.isSingleton()) {
			Class<?> klass = beanDefinition.getKlass();
			try {
				Object obj =klass.newInstance();
				beanDefinition.setObject(obj);
			} catch (InstantiationException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			}
		}
		if (!beanDefinition.isInject() || !beanDefinition.isSingleton()) {
			beanDefinition.setInject(true);
			inject(beanDefinition);
		}
			
		return (T) object;
	}
	
	private static void inject(BeanDefinition beanDefinition) {
		injectField(beanDefinition);
		injectMethod(beanDefinition);
	}
	
	private static void injectField(BeanDefinition beanDefinition) {
		Class<?> klass = beanDefinition.getKlass();
		Field[] fields = klass.getDeclaredFields();
		for (Field field : fields) {
			if (!field.isAnnotationPresent(Autowired.class)) {
				continue ;
			}
			 Object value = getBean(field.getType());
			 field.setAccessible(true);
			 try {
				field.set(beanDefinition.getObject(), value);
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			}
		}
	}
	
	private static void injectMethod(BeanDefinition beanDefinition) {
		Class<?> klass = beanDefinition.getKlass();
		Method[] methods = klass.getDeclaredMethods();
		
		for (Method method : methods) {
			if (!method.isAnnotationPresent(Autowired.class) || method.getParameterCount() != 1
					|| !method.getName().startsWith("set")) {
				continue ;
			}
			Parameter[] parameters = method.getParameters();
			Class<?> paraKlass = parameters[0].getType();
			Object obj = getBean(paraKlass);
			try {
				method.invoke(beanDefinition.getObject(),obj);
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (InvocationTargetException e) {
				e.printStackTrace();
			}
		}
	}

上述代码可见,我们默认的inject是false,我们在getBean方法里面先检测inject是否为false,如果为false,那么证明这个类的成员和方法还没有注入过,那么就需要进行注入。我们通过inject()方法进行注入,最值得注意的是,我们在注入之前,先设置inject为true,这就是避免循环注入的基础。因为当注入C类的成员的时候,会需要A类型的BeanDefinition,我们检测A的inject为true,条件不满足,所以,循环会停止,注入也就会很正确的完成。
对于单例,大家已经发现,上述所给的BeanDefinition类里多了一个singleton成员,这个成员默认为true,我们在getBean()方法中取得这个成员的时候,检测是否为单例,如果不是,我们需要新生成一个对象,这个对象会覆盖原来的object,由此,我们新得到了一个对象;若是单例,我们就继续取得这个原对象,这就是单例的。由此,我们解决了单例和非单例问题。
其实对于发现循环依赖关系这个问题,我们在博客【JavaEE】自主实现Spring核心IOC和AOP之实现类的注入中已经实现的差不多了。因为我们当时采取的操作是:
1.包扫描遍历所有的类,将含有Component注解的类加入到beanMap中,同时生成该类对应的BeanDefinition。
2.在每一次扫描完Component注解后,仍在该类进行Bean注解的扫描,一旦有Bean注解了,先从beanPool里面获取该方法的参数值,如果全能得到,将该方法形成MethodDefinition加入到okMethod列表里;如果得不到,就放入到unOkMethod列表里,并且,将以未取得参数值的类型为键,建立一个List且类型为MethodDefinition,将方法形成的MethodDefinition加入进去,然后继续进行遍历。
3.当所有的遍历都完毕后,开始执行okMethod列表里的方法,执行完以后,将返回值类型,再次形成一个键值对,放到beanPool里面。然后循环操作,直到okMethod列表空为止。这时,如果unOkMethod列表中还有为解决的类,那么这些类,有两种可能性,一种是压根就没有,第二种就是存在循环依赖关系,对于这种存在依赖关系的类,我们应该交给用户来处理。
所以,我们应该在getBean()方法中加入一个输出(或抛出一个异常)。

	private void showCircleDependency() {
		System.out.println(MethodDependence.getUndependence());
	}
	
	@SuppressWarnings("unchecked")
	public <T> T getBean(String className) {
		BeanDefinition beanDefinition = beanPool.get(klassName);
		if (beanDefinition == null) {
			showCircleDependency();
			System.out.println("Bean[" + className + "]不存在!");
			return null;
		}
		
		Object object = beanDefinition.getObject();
		if (!beanDefinition.isSingleton()) {
			Class<?> klass = beanDefinition.getKlass();
			try {
				Object obj =klass.newInstance();
				beanDefinition.setObject(obj);
			} catch (InstantiationException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			}
		}
		if (!beanDefinition.isInject() || !beanDefinition.isSingleton()) {
			beanDefinition.setInject(true);
			inject(beanDefinition);
		}
			
		return (T) object;
	}
	//	MethodDependence类应增加一个方法!
static String getUndependence() {
		StringBuffer str = new StringBuffer();
		
		for (Class<?> dependenceClass : dependenceMethodPool.keySet()) {
			List<MethodDefinition> mdList = dependenceMethodPool.get(dependenceClass);
			for (MethodDefinition md : mdList) {
				str.append(md.getMethod())
				.append("  --> ").append(dependenceClass.getName())
				.append('\n');
			}
		}
		
		return str.toString();
	}

接下来,我们来做一个测试:

//  对例子中的类加入Component注解,对类中的成员加上Autowired注解
public static void main(String[] args) {
		BeanFactory.scanPackage("com.mec.mpring.demo");
		
		BeanFactory beanFactory = new BeanFactory();
		
		A a = beanFactory.getBean(A.class);
		B b = beanFactory.getBean(B.class);
		C c = beanFactory.getBean(C.class);
		
		System.out.println("a:" + a);
		System.out.println("b : " + b);
		System.out.println("c:" + c);
]

结果:
在这里插入图片描述

@Component
public class One {
	
	public One() {
	}
	
	@Bean
	public Two getTwo(Three three) {
		return new Two();
	}
	@Bean
	public Three getThree(Two two) {
		return new Three();
	}
}
public class Two {

	public Two() {
	}

	@Override
	public String toString() {
		return "这是Two类 ";
	}

}
public class Three {

	public Three() {
	}

	@Override
	public String toString() {
		return "这是Three类";
	}
}
	public static void main(String[] args) {
		BeanFactory.scanPackage("com.mec.mpring.demo");
		
		BeanFactory beanFactory = new BeanFactory();
		
		Two two = beanFactory.getBean(Two.class);
		System.out.println(two);
		System.out.println("----------------------------");
		Three three = beanFactory.getBean(Three.class);
		System.out.println(three);
	}

结果如下:
在这里插入图片描述由结果可知,模拟Spring核心IOC之实现循环注入、单例、发现循环依赖正确。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值