初始化bean

思路

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

// 在当前类及父类进行字段和方法注入:
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);
}
}

在注入单个属性中,

  1. 要注意检查属性的表示,比如要禁止静态方法或者字段的注入,以及final方法和字段的注入。
  2. 同时要注意开启暴力反射

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

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:

// 创建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

// 创建BeanPostProcessor类型的Bean:
List<BeanPostProcessor> 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。

// 创建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方法

public interface BeanPostProcessor {
// 注入依赖时,应该使用的Bean实例:
default Object postProcessOnSetProperty(Object bean, String beanName) {
return bean;
}
}

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

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

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

// 代理Bean:
class FirstProxyBean extends OriginBean {
final OriginBean target;

public FirstProxyBean(OriginBean target) {
this.target = target;
}
}

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

Object getProxiedInstance(BeanDefinition def) {
Object beanInstance = def.getInstance();
// 如果Proxy改变了原始Bean,又希望注入到原始Bean,则由BeanPostProcessor指定原始Bean:
List<BeanPostProcessor> reversedBeanPostProcessors = new ArrayList<>(this.beanPostProcessors);
Collections.reverse(reversedBeanPostProcessors);
for (BeanPostProcessor beanPostProcessor : reversedBeanPostProcessors) {
Object restoredInstance = beanPostProcessor.postProcessOnSetProperty(beanInstance, def.getName());
if (restoredInstance != beanInstance) {
beanInstance = restoredInstance;
}
}
return beanInstance;
}

疑惑,为什么排序就能找到原始的bean去了

处理多次代理的情况,即一个原始Bean,比如UserService,被一个事务处理的BeanPostProcsssor代理为UserServiceTx,又被一个性能监控的BeanPostProcessor代理为UserServiceMetric,还原的时候,对BeanPostProcsssor做一个倒序,先还原为UserServiceTx,再还原为UserService。

spring为什么使用的是三级缓存

bean的标准生命周期是如下
标准bean的生命周期
有循环依赖的bean的生命周期是
有循环依赖的bean的生命周期
也就是说,其实在spring的设计中,BeanPostProcessor的代理,要求的是在bean完成初始化之后再生成代理类。

具体代码如下:

//设置实例
def.setInstance(instance);
//直接开始了检查代理
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();

总结

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

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