模拟Spring之循环依赖的实现

模拟Spring之循环依赖

如果类与类之间存在依赖关系,比如说,A类中有B类类型的成员,B类中有C类类型的成员,C类中又有A类类型的成员,形成了一个“闭环”式的循环依赖关系。
在这种情况下去处理的话,其实就对应着上一篇文章中提到的关于Bean注解的第三种应用场合。模拟Spring之Bean注解的引入
也就是,类的对象可能暂时不可用,无法注入,因为我们不确定该类的成员是否已经注入。那么,这种情况下,依然使用Bean注解来解决。

两个存在依赖关系的类:

public class One {
	private Two two;
	
	public One() {
	}

	public Two getTwo() {
		return two;
	}

	public void setTwo(Two two) {
		this.two = two;
	}
	
}
public class Two {
	private One one;
	
	public Two() {
	}

	public One getOne() {
		return one;
	}

	public void setOne(One one) {
		this.one = one;
	}
	
}

这两个类非常的简单,主要是分别有对方的类类型的成员,构成依赖关系。
延续之前文章的代码,即,在Config类中增加两个Bean注解的方法:

	@Bean
	public One getOne(Two two) {
		System.out.println("这里处理有依赖关系的One类!");
		One one = new One();
		one.setTwo(two);
		
		return one;
	}
	
	@Bean
	public Two getTwo(One one) {
		System.out.println("这里处理有依赖关系的Two类!");
		Two two = new Two();
		two.setOne(one);
		
		return two;
	}

写到这就会发现,我们之前写的对于Bean注解的处理就不足以处理了 。回想之前的dealBean方法,我们只处理了不带参数的方法,那现在,就得开始处理带参方法了。

			if (method.getParameterCount() > 0) {
				//处理带参方法;
				dealMethodWithPara(method, object);
				continue;
			}

处理带参方法,首先考虑我们一开始给的两个类是有依赖关系的,也就是说,不确定方法的参数是否已经注入到beanPool中,也不知道在扫描过程中具体什么时候完成注入,也可能需要扫描好几个包之后才能注入。所以,对于方法的执行就需要在包扫描结束之后。
再者,需要等到方法的参数全部都准备好,即都在beanPool中之后,该方法才能执行,而参数的注入时间不一定相同,这时信息需要及时“反馈”。

综上所述,对于存在依赖关系的注入,或者说就是相应Bean注解的带参方法的执行,需要另外构建“三个容器”,分别是:
一个用来存储暂时不能执行的方法对应的MethodDefinition的List;
一个用来存储可以执行的方法对应的MethodDefinition的List;
一个以参数类型为键,以参数所属方法的MethodDefinition形成的List为值的Map;

首先构建每一个方法对应的MethodDefinition:

public class MethodDefinition {
	private Method method;
	private Object object;
	private int paraCount;
	
	MethodDefinition() {
		this.paraCount = 0;
	}

	Method getMethod() {
		return method;
	}

	void setMethod(Method method) {
		this.method = method;
	}

	Object getObject() {
		return object;
	}

	void setObject(Object object) {
		this.object = object;
	}

	int getParaCount() {
		return paraCount;
	}

	void setParaCount(int paraCount) {
		this.paraCount = paraCount;
	}
	
	int sub() {
		return --paraCount;
	}
	
}

这里的method和object是很容易想到的成员,最重要的是还有一个paraCount,即参数个数。这个paraCount用来记录该方法中“尚未满足的参数个数”,每满足一个,即注入到beanPool中,paraCount就减1,当paraCount减为0时,则意味着该方法可以执行了。

接下来,实现上面提到的“三个容器”,两个List和一个Map,将其封装起来。

public class MethodDependence {
	private static final List<MethodDefinition> uninvokeMethodDefinitionList 
									= new ArrayList<MethodDefinition>();
	private static final List<MethodDefinition> invokeMethodDefinitionList 
									= new LinkedList<MethodDefinition>();
	private static final Map<Class<?>, List<MethodDefinition>> dependenceMethodPool 
									= new HashMap<Class<?>, List<MethodDefinition>>();
	
	MethodDependence() {
	}
	
	static void adduninvokeMethod(Map<Class<?>, Integer> paraPool, 
									MethodDefinition methodDefinition) {
		uninvokeMethodDefinitionList.add(methodDefinition);
		
		for (Class<?> paraType : paraPool.keySet()) {
			if (!dependenceMethodPool.containsKey(paraType)) {
				List<MethodDefinition> mdList = new ArrayList<MethodDefinition>();
				dependenceMethodPool.put(paraType, mdList);
			}
			List<MethodDefinition> methodList = dependenceMethodPool.get(paraType);
			methodList.add(methodDefinition);
		}
	}
	
	static void checkDependence(Class<?> beanClass) {
		List<MethodDefinition> methodDefinitionList = dependenceMethodPool.get(beanClass);
		if (methodDefinitionList == null) {
			return;
		}
		List<MethodDefinition> okMethodList = new ArrayList<MethodDefinition>();
		for (MethodDefinition md : methodDefinitionList) {
			if (md.sub() == 0) {
				okMethodList.add(md);
			}
		}
		if (!okMethodList.isEmpty()) {
			for (MethodDefinition method : okMethodList) {
				uninvokeMethodDefinitionList.remove(method);
				invokeMethodDefinitionList.add(method);
			}
		}
		dependenceMethodPool.remove(beanClass);
	}
	
	static void invokeDependenceMethod() {
		while (!invokeMethodDefinitionList.isEmpty()) {
			MethodDefinition methodDefinition = invokeMethodDefinitionList.get(0);
			Method method = methodDefinition.getMethod();
			Object object = methodDefinition.getObject();
			invokeMethodDefinitionList.remove(0);
			
			BeanFactory1.invokeMethodWithPara(method, object);
		}
	}
		
}

针对以上MethodDependence类相应方法做如下简述:
操作思路过程简述如下:
1.遇到了一个带参方法,检测其所有的参数,如果参数能满足,则暂时不处理,如果参数不能满足,则将其存储到Map中,以该参数类型作为键,对应的方法的MethodDefinition形成的一个List作为值;
2.对该方法的所有参数进行了检测之后,若该方法存在不能满足的参数,则将该方法的MethodDefinition存储到第一个List中,即uninvokeMethodDefinitionList ;若该方法的参数都满足,则将MethodDefinition存储到第二个List中,即invokeMethodDefinitionList ;
3.每处理完一个bean,都要扫描dependenceMethodPool ,将依赖这个bean的MethodDefinition的paraCount减一,当paraCount为0,就可以将方法的MethodDefinition存储到invokeMethodDefinitionList中,等待执行。

而在BeanFactory中就需要具体处理带参方法,

	/**
	 * 这个方法是为了将方法中暂时没有在BeanPool里的参数存到一个paraPool中;
	 * 值得注意的是Map不能一边遍历一边删除,所以解决方案是再提供一个klassList;
	 * 这个klassList存储的是BeanPool里已经有的参数,即即将被paraPool删除的参数类型;
	 * 也就是说,paraPool最终存储的是方法的暂时不满足的参数(BeanPool里暂时没有的参数);
	 * @param method
	 * @return
	 */
	private static Map<Class<?>, Integer> getMethodPara(Method method) {
		Map<Class<?>, Integer> paraPool = new HashMap<Class<?>, Integer>();
		
		Class<?>[] paraTypes = method.getParameterTypes();
		for (int index = 0; index < paraTypes.length; index++) {
			paraPool.put(paraTypes[index], 0);
		}
		List<Class<?>> klassList = new ArrayList<Class<?>>();
		for (Class<?> type : paraPool.keySet()) {
			BeanDefinition beanDefinition = BeanPool.get(type.getName());
			if (beanDefinition != null) {
				klassList.add(type);
			}
		}
		for (Class<?> klass : klassList) {
			paraPool.remove(klass);
		}
		
		return paraPool;
	}
	
	/**
	 * 执行带参方法;
	 * 注意方法执行后的返回值是一个bean,得put进池子里;
	 * @param method
	 * @param object
	 */
	static void invokeMethodWithPara(Method method, Object object) {
		Class<?>[] paraTypeArray = method.getParameterTypes();
		int length = paraTypeArray.length;
		Object[] paraValues = new Object[length];
		
		for (int index = 0; index < length; index++) {
			String className = paraTypeArray[index].getName();
			BeanDefinition beanDefinition = getBeanObject(className);
			paraValues[index] = beanDefinition.getObject();
		}
		try {
			Object bean = method.invoke(object, paraValues);
			Class<?> returnType = method.getReturnType();
			BeanDefinition beanDefinition = new BeanDefinition();
			beanDefinition.setKlass(returnType);
			beanDefinition.setObject(bean);
			
			BeanPool.put(returnType.getName(), beanDefinition);
			
			MethodDependence.checkDependence(returnType);
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 处理带参方法;
	 * 如果参数都在classBeanPool中已存在,则执行;
	 * 否则,生成一个MethodDefinition,并加到尚不能执行方法的List中;
	 * @param method
	 * @param object
	 */
	private static void dealMethodWithPara(Method method, Object object) {
		Map<Class<?>, Integer> paraMap = getMethodPara(method);
		
		if (paraMap == null) {
			invokeMethodWithPara(method, object);
			return;
		}
		MethodDefinition methodDefinition = new MethodDefinition();
		methodDefinition.setMethod(method);
		methodDefinition.setObject(object);
		methodDefinition.setParaCount(paraMap.size());
		
		MethodDependence.adduninvokeMethod(paraMap, methodDefinition);
	}

上述代码中相应方法的注释对其功能做了简述。

至此,有关带参方法的处理就简述于此。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值