文章目录
前言
相信凡是写Java的同学都知道或者背过Spring实现循环依赖的方式吧,无非是三个缓存工厂,然后每个工厂放object的什么形态,bla-bla之类的。之前我也写过一篇博客,图解过 。不过,如果我们自己来实现,该如何写呢?接着我们上一篇的博客,继续探索一下。
一、前期辅助
1.1 最朴素的想法
既然我们需要实现一个循环依赖,那么我们首先来写个demo
public class CycleService1{
private CycleService2 cycleService2;
}
public class CycleService2{
private CycleService1 cycleService1;
}
那我们必然想实现的是CycleService1在注入CycleService2的时候,能够获取到CycleService1,但是此时其实CycleService1还只是一个属性为空的object,暂且称之为earlyObject,还没有放入到我们的BEAN_MAP,那么最朴素直观的想法就是,如果我们暂时把这个earlyObject放到某个容器NON_FIELD_BEAN_MAP中,最后等这个earlyObject的属性设置好了之后,再把它从earlyObject从NON_FIELD_BEAN_MAP 中移动到BEAN_MAP中。
那我们对之前的MyContainer进行一下简单的改造
@Slf4j
public class MyContainer1 {
/**
* 保存所有需要处理的类信息:包括类以及他们需要设置的属性
*/
private static Map<Class, MetaNode> META_NODE_MAP = new HashMap<>();
/**
* 保存最终生成的实例
*/
private static Map<Class, Object> BEAN_MAP = new HashMap<>();
private static Map<Class, Object> NON_FIELD_BEAN_MAP = new HashMap<>();
public static void registerClass(Class clazz, List<Field> fieldList) {
if (!META_NODE_MAP.containsKey(clazz)) {
MetaNode metaNode = new MetaNode();
metaNode.setClazz(clazz);
Map<String, Field> fieldMap = CollectionUtils.emptyIfNull(fieldList).stream()
.collect(Collectors.toMap(Field::getName, Function.identity()));
metaNode.setFieldMap(fieldMap);
META_NODE_MAP.putIfAbsent(clazz, metaNode);
}
}
public static Object getBean(Class clazz) {
Object o = BEAN_MAP.get(clazz);
if (Objects.nonNull(o)) {
return o;
}
return NON_FIELD_BEAN_MAP.get(clazz);
}
public static void main(String[] args) {
try {
System.out.println(BEAN_MAP.values());
System.out.println("init==========");
// 1. 注册需要处理的class
registerClass();
// 2. 遍历所有需要处理的class,然后进行 instance 和 inject
for (Class clazz : MapUtils.emptyIfNull(META_NODE_MAP).keySet()) {
if (BEAN_MAP.containsKey(clazz)) {
continue;
}
Object instance = complete(clazz);
NON_FIELD_BEAN_MAP.entrySet().removeIf(it -> it.getKey().equals(clazz));
BEAN_MAP.putIfAbsent(clazz, instance);
}
System.out.println(BEAN_MAP.values());
System.out.println("done==========");
} catch (Exception e) {
log.error("Oops, there is a error", e);
}
}
private static Object complete(Class clazz) throws Exception {
MetaNode metaNode = META_NODE_MAP.get(clazz);
if (Objects.isNull(metaNode)) {
return null;
}
// 1. 实例化
Object instance = instance(clazz);
if (MapUtils.isNotEmpty(metaNode.getFieldMap())) {
// 2. 属性设置
injectField(instance, metaNode.getFieldMap());
}
// 3. aop
instance = wrapIfNecessary(clazz, instance);
return instance;
}
private static void registerClass() throws Exception{
registerClass(CycleService1.class, Collections.singletonList(CycleService1.class.getDeclaredField("cycleService2")));
registerClass(CycleService2.class, Collections.singletonList(CycleService2.class.getDeclaredField("cycleService1")));
}
private static Object instance(Class clazz) throws Exception{
Object o = clazz.newInstance();
NON_FIELD_BEAN_MAP.putIfAbsent(clazz, o);
return o;
}
private static void injectField(Object instance, Map<String, Field> fieldMap) throws Exception {
for (Map.Entry<String, Field> fieldEntry : MapUtils.emptyIfNull(fieldMap).entrySet()) {
Field field = fieldEntry.getValue();
Class<?> type = field.getType();
Object toInject = getBean(type);
if (Objects.isNull(toInject)) {
toInject = complete(type);
BEAN_MAP.putIfAbsent(type, toInject);
}
field.setAccessible(true);
field.set(instance, toInject);
}
}
private static Object wrapIfNecessary(Class clazz, Object object) {
Class[] interfaces = clazz.getInterfaces();
if (Objects.isNull(interfaces) || interfaces.length == 0) {
return object;
}
Object proxy = Proxy.newProxyInstance(clazz.getClassLoader(), interfaces, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("it is a proxy doing its job:" + method.getName());
Object invoke = method.invoke(object, args);
return invoke;
}
});
return proxy;
}
}
我们的改造分为3步:
1. instance里面在对对象进行了基本的raw bean生成之后,放入到NON_FIELD_BEAN_MAP
2. 增加一个getBean方法,先从BEAN_MAP中获取对象,再从NON_FIELD_BEAN_MAP中获取,所以在注入属性的时候调用该方法
3. 在最后阶段,从NON_FIELD_BEAN_MAP中remove掉对应的earlyObject,在BEAN_MAP中加入object
1.2 真的这么丝滑么
如果这么简单,那么spring为什么做得比我们复杂?
思考下面这个问题:
如果需要注入的对象是需要进行aop的呢?
我们目前貌似只实现了最基本的容器产生bean,以及这些bean之间的依赖注入,如果CycleService1和CycleService2都需要进行aop呢?上面的代码还能顺利运行么?
答案是 NO!!!
二、当注入属性是proxy
2.1 为什么?
1.2 我们提到了直接进行proxy是不行的,为什么呢?其实是因为我们的demo是基于jdk proxy来做的proxy,所以生成的proxy其实是raw class的接口instance了,我们在spring容器里的bean的class不再是 raw class了,所以注入的时候会报错。
2.2 第一种改造
2.2.1 循环依赖属性修改
为了不影响前面的demo,我们又设置了新的循环依赖类,属性注入的是接口类
public class CycleService3 implements MyInterface3{
private MyInterface4 cycleService4;
@Override
public void doSomething() {
System.out.println("this is CycleService3:" + this.getClass() +"; my cycleService4 is:" + cycleService4.getClass());
}
}
public class CycleService4 implements MyInterface4{
private MyInterface3 cycleService3;
@Override
public void doSomething() {
System.out.println("this is CycleService4:" + this.getClass() +"; my cycleService3 is:" + cycleService3.getClass());
}
}
2.2.2 class元信息修改
因为涉及到proxy,而且这个proxy还得被其他类作为属性注入,所以我们的类元信息需要显式表现出类的接口。第一版demo之所以没有,也是因为不需要被依赖注入。
public class MetaNode {
private Class clazz;
private Map<String, Field> fieldMap;
// 类实现了的接口类
private List<Class> interfaces;
}
2.2.3 所以proxy的发生时机
因为属性注入必须注入的是proxy bean,那么在属性注入的时候就要获取到依赖的类的proxy类。
具体实现如下:
public class MyContainer3 {
/**
* 保存所有需要处理的类信息:包括类以及他们需要设置的属性
*/
private static Map<Class, MetaNode> META_NODE_MAP = new HashMap<>();
/**
* 保存最终生成的实例
*/
private static Map<Class, Object> BEAN_MAP = new HashMap<>();
private static Map<Class, Object> NON_FIELD_BEAN_MAP = new HashMap<>();
private static Set<Class> CACHE_PROXYED = new HashSet<>();
public static void registerClass(Class clazz, List<Field> fieldList, List<Class> interfaces) {
if (!META_NODE_MAP.containsKey(clazz)) {
MetaNode metaNode = new MetaNode();
metaNode.setClazz(clazz);
Map<String, Field> fieldMap = CollectionUtils.emptyIfNull(fieldList).stream()
.collect(Collectors.toMap(Field::getName, Function.identity()));
metaNode.setFieldMap(fieldMap);
metaNode.setInterfaces(ListUtils.emptyIfNull(interfaces));
META_NODE_MAP.putIfAbsent(clazz, metaNode);
}
}
public static Object getBean(Class clazz) {
Object o = BEAN_MAP.get(clazz);
if (Objects.nonNull(o)) {
return o;
}
return NON_FIELD_BEAN_MAP.get(clazz);
}
public static void main(String[] args) {
try {
System.out.println(BEAN_MAP.values());
System.out.println("init==========");
// 1. 注册需要处理的class
registerClass();
// 2. 遍历所有需要处理的class,然后进行 instance 和 inject
for (Map.Entry<Class, MetaNode> entry: MapUtils.emptyIfNull(META_NODE_MAP).entrySet()) {
if (BEAN_MAP.containsKey(entry.getKey())) {
continue;
}
Object instance = complete(entry.getKey());
addToContext(entry.getKey(), instance);
}
System.out.println(BEAN_MAP.values());
System.out.println("done==========");
} catch (Exception e) {
log.error("Oops, there is a error", e);
}
}
private static void addToContext(Class clazz, Object instance) {
Set<Class> clazzSet = new HashSet<>();
clazzSet.add(clazz);
if (!clazz.isInterface()) {
clazzSet.addAll(Arrays.asList(clazz.getInterfaces()));
}
NON_FIELD_BEAN_MAP.entrySet().removeIf(it -> clazzSet.contains(it.getKey()));
BEAN_MAP.putIfAbsent(clazz, instance);
}
private static Object complete(Class clazz) throws Exception {
MetaNode metaNode = META_NODE_MAP.get(clazz);
if (Objects.isNull(metaNode)) {
return null;
}
// 如果容器里已经有了对应的数据,那么就返回
if (getBean(metaNode.getClazz()) != null) {
return getBean(metaNode.getClazz());
}
// 1. 实例化
Object instance = instance(metaNode.getClazz());
if (MapUtils.isNotEmpty(metaNode.getFieldMap())) {
// 2. 属性设置
injectField(instance, metaNode.getFieldMap());
}
// 3. aop
Object proxyed = wrapIfNecessary(metaNode.getClazz(), instance);
if (instance == proxyed) {
if (!BEAN_MAP.containsKey(metaNode.getClazz())) {
throw new Exception(String.format("clazz %s have been aop proxyed more than once", clazz));
} else {
return BEAN_MAP.get(metaNode.getClazz());
}
}
return proxyed;
}
private static void registerClass() throws Exception{
registerClass(CycleService3.class, Collections.singletonList(CycleService3.class.getDeclaredField("cycleService4")), Collections.singletonList(MyInterface3.class));
registerClass(CycleService4.class, Collections.singletonList(CycleService4.class.getDeclaredField("cycleService3")), Collections.singletonList(MyInterface4.class));
}
private static Object instance(Class clazz) throws Exception{
Object o = clazz.newInstance();
NON_FIELD_BEAN_MAP.putIfAbsent(clazz, o);
return o;
}
private static void injectField(Object instance, Map<String, Field> fieldMap) throws Exception {
for (Map.Entry<String, Field> fieldEntry : MapUtils.emptyIfNull(fieldMap).entrySet()) {
Field field = fieldEntry.getValue();
Class<?> type = field.getType();
type = findRealInjectClass(type);
Object toInject = complete(type);
field.setAccessible(true);
/**
* 此处正是使用三级工厂的原因,如果只使用二级工厂
* 那么此时譬如C1和C2循环依赖,当C2注入了一个proxy的C1之后,没有保存到某个地方
* 再回到C1的complete过程中,此时会导致最后容器里的proxy-C1和C2注入的proxy-C1不是同一个对象
* 我此处是让C2注入的proxy-C1直接覆盖了一级工厂(BEAN_MAP)里的对象,
* 然后在回到C1的complete过程中,直接判断有没有一级工厂里有没有C1,如果有就直接返回,也就不会进行proxy的过程了
*/
Object o = wrapIfNecessary(type, toInject);
BEAN_MAP.putIfAbsent(type, o);
field.set(instance, o);
}
}
public static Class findRealInjectClass(Class clazz) throws Exception {
if (!clazz.isInterface()) {
return clazz;
}
// 如果是接口,找到其实现类
List<Class> satisfiedClazzList = new LinkedList<>();
for (Map.Entry<Class, MetaNode> entry : META_NODE_MAP.entrySet()) {
if (entry.getValue().getInterfaces().contains(clazz)) {
satisfiedClazzList.add(entry.getKey());
}
}
// 同一个接口,不允许多个实现类,如果存在多个,spring的处理方式是判断有没有@Primary注解
if (CollectionUtils.size(satisfiedClazzList) != 1) {
throw new Exception("can not find satisfiedClazz for class:" + clazz.getSimpleName());
}
return satisfiedClazzList.get(0);
}
private static Object wrapIfNecessary(Class clazz, Object object) {
if (CACHE_PROXYED.contains(clazz)) {
return object;
}
Class[] interfaces = clazz.getInterfaces();
if (Objects.isNull(interfaces) || interfaces.length == 0) {
return object;
}
Object proxy = Proxy.newProxyInstance(clazz.getClassLoader(), interfaces, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("it is a proxy doing its job:" + method.getName());
Object invoke = method.invoke(object, args);
return invoke;
}
});
CACHE_PROXYED.add(clazz);
return proxy;
}
}
大家可以发现逻辑上面多了几个功能:
- findRealInjectClass,这个方法是当需要注入的属性传入的class是接口的时候,我们需要查找到其真正的实现类,否则无法进行实例化
- 我们在主体流程第三步的wrapIfNecessary之后进行了一个proxy和instance的比较。此处比较的意义呢,就是防止同一个instance被spring进行了多次proxy,对于默认单例的spring容器这是不可接受的
- 有没有发现,我们只使用了两个缓存工厂便简单实现了循环依赖??其实是因为我们在某个类因为被其他类依赖注入并proxy之后,直接放入了spring的最终容器BEAN_MAP中,而没有放到一个新的二级缓存工厂中。spring使用二级缓存工厂的出发点应该是因为觉得只有生命周期完成的bean才有资格放入到最终容器中。
2.3 如果先proxy再inject呢?
刚刚2.2提到我们在某个类因为被其他类依赖注入并proxy之后,直接放入了spring的最终容器BEAN_MAP中,因此猜想出spring容器使用到二级缓存工厂的原因。
也许有人问了,那如果我们把主体流程改为:1. instance; 2. proxy; 3. inject。同时在instance之后不往一级缓存工厂里put,只有proxy之后才put,是不是也能满足只有生命周期完成的bean才会放到最终容器中?
public class MyContainer5 {
/**
* 保存所有需要处理的类信息:包括类以及他们需要设置的属性
*/
private static Map<Class, MetaNode> META_NODE_MAP = new HashMap<>();
/**
* 保存最终生成的实例
*/
private static Map<Class, Object> BEAN_MAP = new HashMap<>();
private static Map<Class, Object> EARLY_BEAN_MAP = new HashMap<>();
private static Set<Class> CACHE_PROXYED = new HashSet<>();
public static void registerClass(Class clazz, List<Field> fieldList, List<Class> interfaces) {
if (!META_NODE_MAP.containsKey(clazz)) {
MetaNode metaNode = new MetaNode();
metaNode.setClazz(clazz);
Map<String, Field> fieldMap = CollectionUtils.emptyIfNull(fieldList).stream()
.collect(Collectors.toMap(Field::getName, Function.identity()));
metaNode.setFieldMap(fieldMap);
metaNode.setInterfaces(ListUtils.emptyIfNull(interfaces));
META_NODE_MAP.putIfAbsent(clazz, metaNode);
}
}
public static Object getBean(Class clazz) {
Object o = BEAN_MAP.get(clazz);
if (Objects.nonNull(o)) {
return o;
}
o = EARLY_BEAN_MAP.get(clazz);
return o;
}
public static void main(String[] args) {
try {
System.out.println(BEAN_MAP.values());
System.out.println("init==========");
// 1. 注册需要处理的class
registerClass();
// 2. 遍历所有需要处理的class,然后进行 instance 和 inject
for (Map.Entry<Class, MetaNode> entry: MapUtils.emptyIfNull(META_NODE_MAP).entrySet()) {
if (BEAN_MAP.containsKey(entry.getKey())) {
continue;
}
Pair<Class, Object> instancePair = complete(entry.getKey());
addToContext(instancePair.getKey(), instancePair.getValue());
}
System.out.println(BEAN_MAP.values());
System.out.println("done==========");
} catch (Exception e) {
log.error("Oops, there is a error", e);
}
}
private static void addToContext(Class clazz, Object instance) {
Set<Class> clazzSet = new HashSet<>();
clazzSet.add(clazz);
if (!clazz.isInterface()) {
clazzSet.addAll(Arrays.asList(clazz.getInterfaces()));
}
EARLY_BEAN_MAP.entrySet().removeIf(it -> it.getKey().equals(clazz));
BEAN_MAP.putIfAbsent(clazz, instance);
}
private static Pair<Class, Object> complete(Class clazz) throws Exception {
clazz = findRealInjectClass(clazz);
MetaNode metaNode = META_NODE_MAP.get(clazz);
if (Objects.isNull(metaNode)) {
return null;
}
// 如果容器里已经有了对应的数据,那么就返回
if (getBean(metaNode.getClazz()) != null) {
return new Pair(clazz, getBean(metaNode.getClazz()));
}
// 1. 实例化
Object instance = instance(metaNode.getClazz());
// 2. aop
Object proxyed = wrapIfNecessary(metaNode.getClazz(), instance);
EARLY_BEAN_MAP.put(clazz, proxyed);
if (MapUtils.isNotEmpty(metaNode.getFieldMap())) {
// 3. 属性设置
injectField(instance, metaNode.getFieldMap());
}
return new Pair<>(clazz, proxyed);
}
private static void registerClass() throws Exception{
registerClass(CycleService3.class, Collections.singletonList(CycleService3.class.getDeclaredField("cycleService4")), Collections.singletonList(MyInterface3.class));
registerClass(CycleService4.class, Collections.singletonList(CycleService4.class.getDeclaredField("cycleService3")), Collections.singletonList(MyInterface4.class));
}
private static Object instance(Class clazz) throws Exception{
Object o = clazz.newInstance();
return o;
}
private static void injectField(Object instance, Map<String, Field> fieldMap) throws Exception {
for (Map.Entry<String, Field> fieldEntry : MapUtils.emptyIfNull(fieldMap).entrySet()) {
Field field = fieldEntry.getValue();
Class<?> type = field.getType();
Pair<Class, Object> toInjectData = complete(type);
field.setAccessible(true);
// 此处的写法有点ugly,因为 findRealInjectClass 会调用多次
BEAN_MAP.put(toInjectData.getKey(), toInjectData.getValue());
field.set(instance, toInjectData.getValue());
}
}
public static Class findRealInjectClass(Class clazz) throws Exception {
if (!clazz.isInterface()) {
return clazz;
}
// 如果是接口,找到其实现类
List<Class> satisfiedClazzList = new LinkedList<>();
for (Map.Entry<Class, MetaNode> entry : META_NODE_MAP.entrySet()) {
if (entry.getValue().getInterfaces().contains(clazz)) {
satisfiedClazzList.add(entry.getKey());
}
}
if (CollectionUtils.size(satisfiedClazzList) != 1) {
throw new Exception("can not find satisfiedClazz for class:" + clazz.getSimpleName());
}
return satisfiedClazzList.get(0);
}
private static Object wrapIfNecessary(Class clazz, Object object) {
if (CACHE_PROXYED.contains(clazz.getName())) {
return object;
}
Class[] interfaces = clazz.getInterfaces();
if (Objects.isNull(interfaces) || interfaces.length == 0) {
return object;
}
Object proxy = Proxy.newProxyInstance(clazz.getClassLoader(), interfaces, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("it is a proxy doing its job:" + method.getName());
Object invoke = method.invoke(object, args);
return invoke;
}
});
CACHE_PROXYED.add(clazz);
return proxy;
}
}
经过实践,确实可行。那我们不免需要发出疑问:为什么spring不是这么干的?
笔者猜测两个原因:
- spring最初版本和我们的第一版思路类似,定下了先inject再proxy的基调,所以后期即使迭代也不修改主体流程,毕竟spring框架不像咱们的demo这么简单,可能一处简单的修改需要大动筋骨
- 其实spring的aop功能是可插拔的,IOC和依赖注入才是其主体功能,毕竟spring有个注解@EnableAspectJAutoProxy,所以可能是不想把proxy的步骤放入到其主体流程中。
总结
不过还是建议读者可以在了解了原理之后进行手动实现,毕竟这样,才会对spring的一些实现及原理有更多思考。
talk is cheap,show me the code
感觉这篇博客最大的作用是解释了spring的循环依赖能不能用两个缓存工厂实现。如果使用两个缓存工厂,共有两个方案,方案如下:
- 提前把还没有进行inject,但是已经proxy了的bean放入到spring的最终容器中
- spring的主体流程里面先proxy再inject
至于为什么不用上述这两种方案,笔者也写了一些猜想和思考。欢迎讨论~
示例代码
代码地址:我的GitHub