思路

在上一节我们实现了bean的强依赖注入,接下来我们要实现的便是弱依赖的注入,也就是字段和setter方法的注入。
我们首先进行注入,但是不进行init的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 在当前类及父类进行字段和方法注入:
void injectProperties(BeanDefinition def, Class clazz, Object bean) {
// 在当前类查找Field和Method并注入:
for (Field f : clazz.getDeclaredFields()) {
tryInjectProperties(def, clazz, bean, f);
}
for (Method m : clazz.getDeclaredMethods()) {
tryInjectProperties(def, clazz, bean, m);
}
// 在父类查找Field和Method并注入:
Class superClazz = clazz.getSuperclass();
if (superClazz != null) {
// 递归调用:
injectProperties(def, superClazz, bean);
}
}

在注入单个属性中,

  • 要注意检查属性的表示,比如要禁止静态方法或者字段的注入,以及final方法和字段的注入。

  • 同时要注意开启暴力反射

等到所有的属性注入完成之后,开始调用init方法

1
2
3
4
void initBean(BeanDefinition def) {
// 调用init方法:
callMethod(def.getInstance(), def.getInitMethod(), def.getInitMethodName());
}

实现BeanPostProcessor
我们已经完成了扫描Class名称、创建BeanDefinition、创建Bean实例、初始化Bean,理论上一个可用的IoC容器就已经就绪。但是对于spring来说,还有一项能力,就是替换是一种特殊Bean,它的作用是根据条件替换某些Bean。

现在的问题在于那个部分要注入代理,那个部分要注入原始bean

两条原则
一个Bean如果被Proxy替换,则依赖它的Bean应注入Proxy,即上图的MvcController应注入UserServiceProxy;
一个Bean如果被Proxy替换,如果要注入依赖,则应该注入到原始对象,即上图的JdbcTemplate应注入到原始的UserService。

基于这个原则,要满足条件1是很容易的,因为只要创建Bean完成后,立刻调用BeanPostProcessor就实现了替换,后续其他Bean引用的肯定就是Proxy了。先改造创建Bean的流程,在创建@Configuration后,接着创建BeanPostProcessor,再创建其他普通Bean:

1
2
3
4
5
6
7
8
9
10
11
12
// 创建Bean实例:
Object instance = ...;
def.setInstance(instance);

// 调用BeanPostProcessor处理Bean:
for (BeanPostProcessor processor : beanPostProcessors) {
Object processed = processor.postProcessBeforeInitialization(def.getInstance(), def.getName());
// 如果一个BeanPostProcessor替换了原始Bean,则更新Bean的引用:
if (def.getInstance() != processed) {
def.setInstance(processed);
}
}··

加入创建beanPostProcessor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 创建BeanPostProcessor类型的Bean:
List processors = this.beans.values().stream()
// 过滤出BeanPostProcessor:
.filter(this::isBeanPostProcessorDefinition)
// 排序:
.sorted()
// 创建BeanPostProcessor实例:
.map(def -> {
return (BeanPostProcessor) createBeanAsEarlySingleton(def);
}).collect(Collectors.toList());
this.beanPostProcessors.addAll(processors);

// 创建其他普通Bean:
createNormalBeans();

同时将createBeanAsEarlySingleton修改,在创建了bean实例之后,调用BeanPostProcessor。

1
2
3
4
5
6
7
8
9
10
11
12
13
// 创建Bean实例:
Object instance = ...;
def.setInstance(instance);

// 调用BeanPostProcessor处理Bean:
for (BeanPostProcessor processor : beanPostProcessors) {
Object processed = processor.postProcessBeforeInitialization(def.getInstance(), def.getName());
// 如果一个BeanPostProcessor替换了原始Bean,则更新Bean的引用:
if (def.getInstance() != processed) {
def.setInstance(processed);
}
}
return def.getInstance();

但是这么写有一个问题,就是所有的bean的在此之后,如果有代理,他的实例都是代理类,如果我们想注入原始的bean,该怎么实现呢?

对于代理类,我们要求他保存原始的bean方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
public interface BeanPostProcessor {
// 注入依赖时,应该使用的Bean实例:
default Object postProcessOnSetProperty(Object bean, String beanName) {
return bean;
}
}

public class FirstProxyBeanPostProcessor implements BeanPostProcessor {
// 保存原始Bean:
Map originBeans = new HashMap<>();

@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) &#123;
if (OriginBean.class.isAssignableFrom(bean.getClass())) &#123;
// 检测到OriginBean,创建FirstProxyBean:
var proxy = new FirstProxyBean((OriginBean) bean);
// 保存原始Bean:
originBeans.put(beanName, bean);
// 返回Proxy:
return proxy;
&#125;
return bean;
&#125;

@Override
public Object postProcessOnSetProperty(Object bean, String beanName) &#123;
Object origin = originBeans.get(beanName);
if (origin != null) &#123;
// 存在原始Bean时,返回原始Bean:
return origin;
&#125;
return bean;
&#125;
&#125;

// 代理Bean:
class FirstProxyBean extends OriginBean &#123;
final OriginBean target;

public FirstProxyBean(OriginBean target) &#123;
this.target = target;
&#125;
&#125;

同时,对于依赖注入的bean中,我们要注入bean的原始类

1
2
3
4
5
6
7
8
9
10
11
12
13
Object getProxiedInstance(BeanDefinition def) &#123;
Object beanInstance = def.getInstance();
// 如果Proxy改变了原始Bean,又希望注入到原始Bean,则由BeanPostProcessor指定原始Bean:
List reversedBeanPostProcessors = new ArrayList<>(this.beanPostProcessors);
Collections.reverse(reversedBeanPostProcessors);
for (BeanPostProcessor beanPostProcessor : reversedBeanPostProcessors) &#123;
Object restoredInstance = beanPostProcessor.postProcessOnSetProperty(beanInstance, def.getName());
if (restoredInstance != beanInstance) &#123;
beanInstance = restoredInstance;
&#125;
&#125;
return beanInstance;
&#125;

疑惑,为什么排序就能找到原始的bean去了
处理多次代理的情况,即一个原始Bean,比如UserService,被一个事务处理的BeanPostProcsssor代理为UserServiceTx,又被一个性能监控的BeanPostProcessor代理为UserServiceMetric,还原的时候,对BeanPostProcsssor做一个倒序,先还原为UserServiceTx,再还原为UserService。

spring为什么使用的是三级缓存
bean的标准生命周期是如下

有循环依赖的bean的生命周期是

也就是说,其实在spring的设计中,BeanPostProcessor的代理,要求的是在bean完成初始化之后再生成代理类。

具体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
//设置实例
def.setInstance(instance);
//直接开始了检查代理
for (BeanPostProcessor processor : beanPostProcessors) &#123;
Object processed = processor.postProcessBeforeInitialization(def.getInstance(), def.getName());
// 如果一个BeanPostProcessor替换了原始Bean,则更新Bean的引用:
// 具体的判断是否是代理类的方式放在每个代理类中
if (def.getInstance() != processed) &#123;
def.setInstance(processed);
&#125;
&#125;
return def.getInstance();

总结
而对于我们的这个小的spring来说,设计思路上就跟原始的spring不同,这份spring的代码对于自动注入的处理,加载逻辑和原始的spring是不同的,原始的springbean加载要更加类似于一个bean一个bean的处理,直到这个bean处理完初始化之后,才会处理下一个。

但对于我们的spring-mini来说,之所以没有那么多的缓存,只使用了一个set加一个map便完成了循环注入的解决。是思路上的不同,我们会先将所有bean实例化,再去注入内容,这也就导致了我们不需要那么多的缓存,同时也给我们带来了AOP代理失效等之类的隐患。