模拟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);
}
上述代码中相应方法的注释对其功能做了简述。
至此,有关带参方法的处理就简述于此。