上篇中已经对于Spring框架有关IOC和DI进行了说明,但是后续对于Bean注解处理方法只涉及到了对不带参数的方法的处理(具体操作可以在上篇文章
中详细回顾),那本篇就继续针对有Bean注解的带参数的方法处理来进一步介绍:
1)“循环依赖”关系的简单介绍
先给出一个简单例子,解释循环依赖的关系,分别给出三个类A,B,C,A类中有B类型的成员,B类中有C类型的成员,C类中有A类型的成员,并给出三个类的toString()方法,方便之后输出;
@Component
public class A {
@AutoWired
private B b;
public B getB() {
return b;
}
public void setB(B b) {
this.b = b;
}
@Override
public String toString() {
return "A类中的b成员:[" + b + "]";
}
}
@Component
public class B {
@AutoWired
private C c;
public B() {
}
public C getC() {
return c;
}
public void setC(C c) {
this.c = c;
}
@Override
public String toString() {
return "B类中的c成员:[" + c + "]";
}
}
@Component
public class C {
@AutoWired
private A a;
public C() {
}
public A getA() {
return a;
}
public void setA(A a) {
this.a = a;
}
@Override
public String toString() {
return "C类中的a成员:" + (a == null ? "null" : "不为null");
}
}
像上述代码所示,三个类之间相互之间都存在依赖关系,就是我们所说的简单的“循环依赖”。
2)Bean注解处理带参方法的思想介绍
接上篇中的内容来继续解释,我们定义了一个Map(BeanPool)用于存放被Component注解的类(即,相关之间有依赖关系的类),一些不能直接加入BeanPool的类通过利用Bean注解来解决,之前我对于Bean注解只把它用在无参方法上,现在需要继续探讨带参方法的处理,希望可以实现通过Bean注解实现无参和带参方法。
解决带参方法问题的大体思想是:
对于某个带参的方法,根据BeanPool看参数满不满足,将不满足的参数加到一个Map(dependenceMethodPool )中,并将此时不能执行的方法放到一个不可执行列表uninvokeMethodList 中,随着扫描的进行,如果有参数满足了,就把参数从Map中删掉,将方法参数个数减1,当参数个数值减为0了,表示该方法可以执行了,再将该方法从不可执行列表uninvokeMethodList 移入到一个可执行列表invokeMethodList 中。
通过图示简单来看一下:
3)Bean注解处理带参方法的具体实现(循环注入)
当扫描到某个带有Bean注解的方法是,先判断该方法有没有带参数,若为无参方法,就按上篇讲到的处理无参方法的操作进行,若带参,先通过方法得到所有参数,把参数类型当成键,值不重要,在这里当成0就可以,放入一个Map中。
注意:
之所以不把参数存方到List列表中,是因为每次将参数放入List,都需要检查一遍前面有没有放入过该参数,如此一来时间复杂度会很高,不推荐使用List方式存储。而用Map来存放的话,是通过键值对的方式,一个键只能对应一个值,当有相同的键加入时Map时,会自动覆盖前面这个键对应的值,不需要再花费时间复杂度去检查有没有,也不存在键的重复,这个过程中,键所对应的的值并不重要,因为我们需要的只是不重复的参数类型。就算某个方法的参数有三个,其中两个参数是同一个类型的,虽然我们把参数在Map中加入了三次,但是实际上Map中存放的只有两种参数类型,这样避免了对相同参数类型的参数重复加入。
遍历Map,根据Map中的参数类型查看该类型是否是BeanPool中的类,如果是的话就表示该类可以自动完成初始化,将这个类先临时存到一个列表中,依次操作,等到整个Map都遍历结束,再根据列表中存放的类将Map中相应的键值对删除,就得到了方法中不满足的参数类型的Map(有一点需要注意,List不能进行边遍历边删除的操作,因为列表没删除一个内容,整个列表中的位置会发生移动,如果边遍历边删除,最多也只能删除列表中的一半内容,在这里不进一步给出验证过程,大家有兴趣的可以自行验证)。具体的代码如下(给出的只是关键代码,不能直接复制运行):
private static Map<Class<?>, Integer> getParaMap(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<?>> tmpParaList = new ArrayList<Class<?>>();
for(Class<?> beanClass : paraPool.keySet(