初始化bean
思路
在上一节我们实现了bean的强依赖注入,接下来我们要实现的便是弱依赖的注入,也就是字段和setter方法的注入。
我们首先进行注入,但是不进行init的使用
void injectProperties(BeanDefinition def, Class<?> clazz, Object bean) { for (Field f : clazz.getDeclaredFields()) { tryInjectProperties(def, clazz, bean, f); } for (Method m : clazz.getDeclaredMethods()) { tryInjectProperties(def, clazz, bean, m); } Class<?> superClazz = clazz.getSuperclass(); if (superClazz != null) { injectProperties(def, superClazz, bean); } }
|
在注入单个属性中,
- 要注意检查属性的表示,比如要禁止静态方法或者字段的注入,以及final方法和字段的注入。
- 同时要注意开启暴力反射
等到所有的属性注入完成之后,开始调用init方法
void initBean(BeanDefinition def) { 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:
Object instance = ...; def.setInstance(instance);
for (BeanPostProcessor processor : beanPostProcessors) { Object processed = processor.postProcessBeforeInitialization(def.getInstance(), def.getName()); if (def.getInstance() != processed) { def.setInstance(processed); } }··
|
加入创建beanPostProcessor
List<BeanPostProcessor> processors = this.beans.values().stream() .filter(this::isBeanPostProcessorDefinition) .sorted() .map(def -> { return (BeanPostProcessor) createBeanAsEarlySingleton(def); }).collect(Collectors.toList()); this.beanPostProcessors.addAll(processors);
createNormalBeans();
|
同时将createBeanAsEarlySingleton修改,在创建了bean实例之后,调用BeanPostProcessor。
Object instance = ...; def.setInstance(instance);
for (BeanPostProcessor processor : beanPostProcessors) { Object processed = processor.postProcessBeforeInitialization(def.getInstance(), def.getName()); if (def.getInstance() != processed) { def.setInstance(processed); } } return def.getInstance();
|
但是这么写有一个问题,就是所有的bean的在此之后,如果有代理,他的实例都是代理类,如果我们想注入原始的bean,该怎么实现呢?
对于代理类,我们要求他保存原始的bean方法
public interface BeanPostProcessor { default Object postProcessOnSetProperty(Object bean, String beanName) { return bean; } }
public class FirstProxyBeanPostProcessor implements BeanPostProcessor { Map<String, Object> originBeans = new HashMap<>();
@Override public Object postProcessBeforeInitialization(Object bean, String beanName) { if (OriginBean.class.isAssignableFrom(bean.getClass())) { var proxy = new FirstProxyBean((OriginBean) bean); originBeans.put(beanName, bean); return proxy; } return bean; }
@Override public Object postProcessOnSetProperty(Object bean, String beanName) { Object origin = originBeans.get(beanName); if (origin != null) { return origin; } return 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(); 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的生命周期是

也就是说,其实在spring的设计中,BeanPostProcessor的代理,要求的是在bean完成初始化之后再生成代理类。
具体代码如下:
def.setInstance(instance);
for (BeanPostProcessor processor : beanPostProcessors) { Object processed = processor.postProcessBeforeInitialization(def.getInstance(), def.getName()); if (def.getInstance() != processed) { def.setInstance(processed); } } return def.getInstance();
|
总结
而对于我们的这个小的spring来说,设计思路上就跟原始的spring不同,这份spring的代码对于自动注入的处理,加载逻辑和原始的spring是不同的,原始的springbean加载要更加类似于一个bean一个bean的处理,直到这个bean处理完初始化之后,才会处理下一个。
但对于我们的spring-mini来说,之所以没有那么多的缓存,只使用了一个set加一个map便完成了循环注入的解决。是思路上的不同,我们会先将所有bean实例化,再去注入内容,这也就导致了我们不需要那么多的缓存,同时也给我们带来了AOP代理失效等之类的隐患。