invokeBeanFactoryPostProcessors(下)
在上一小节我们分析完了invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);这个方法干了那些事情,现在我们继续对后文做分析,如图:上图Spring进行循环,判断是否有遗漏掉的后置处理器没有解析,一般情况下,currentRegistryProcessors会是0,我们运行代码都会直接跳过,继续往下走:
这里是Spring最后一次对BeanDefinitionRegistryPostProcessor后置处理器进行解析,主要是针对一些父类层级关系,通常也为0,所以依旧不会有什么作用。好了,接下来到了关键代码了
我们对invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);进行跟踪->postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) ,我们将代码停在下图:
注意AppConfig.class的变化
这里它还是一个普通的类,我们调试下一步
可以发现,Spring将其增强成为了cglib增强类,那么AppConfig为什么会增强呢?我们下面进行详细分析
@Configuration
当我们将 @Configuration取消时,
我们调试停在上面的代码
可以看到这里的配置类并没有被增强,所以可以得出结论,我们的@Configuration可以将我们的普通类进行增强,不知读者是否还记得我们在上一章所讲full吗,如果是 @Configuration类,Spring会认为这是一个配置类就会将其标识full,而这个full就是代表配置类是否需要增强,后面我们会在源码中进行证明,好了,说了这么多,相信读者应该对 @Configuration
有一定的了解,现在我们来探讨为什么Spring要增强 配置类,这里我们对加上@Configuration和取消@Configuration进行测试,我们看下面代码:
第一种情况-》取消@Configuration
第二种情况-》加上@Configuration
我们可以看到有了@Configuration,我们的user1()和user2()方法返回的对象是同一个对象,当然这里还有第三种情况加上static,读者可以自行测试看看又是什么情况,结果会发现代理功能会失效,至于为什么会失效我们在后面的源码会详细说明。好了我们大概知道了@Configuration的作用,那么Spring是如何对@Configuration进行增强的呢,我们回到源码中:
进入enhanceConfigurationClasses
public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {
Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<>();
//找到config注解类,这里就可以看到full和lite了,如果full会变成增强类,目的是为了实现单例
for (String beanName : beanFactory.getBeanDefinitionNames()) {
BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
if (ConfigurationClassUtils.isFullConfigurationClass(beanDef)) {
if (!(beanDef instanceof AbstractBeanDefinition)) {
throw new BeanDefinitionStoreException("Cannot enhance @Configuration bean definition '" +
beanName + "' since it is not stored in an AbstractBeanDefinition subclass");
}
else if (logger.isInfoEnabled() && beanFactory.containsSingleton(beanName)) {
logger.info("Cannot enhance @Configuration bean definition '" + beanName +
"' since its singleton instance has been created too early. The typical cause " +
"is a non-static @Bean method with a BeanDefinitionRegistryPostProcessor " +
"return type: Consider declaring such methods as 'static'.");
}
configBeanDefs.put(beanName, (AbstractBeanDefinition) beanDef);
}
}
if (configBeanDefs.isEmpty()) {
// nothing to enhance -> return immediately
return;
}
ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer();
for (Map.Entry<String, AbstractBeanDefinition> entry : configBeanDefs.entrySet()) {
AbstractBeanDefinition beanDef = entry.getValue();
// If a @Configuration class gets proxied, always proxy the target class
beanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
try {
// Set enhanced subclass of the user-specified bean class
Class<?> configClass = beanDef.resolveBeanClass(this.beanClassLoader);
if (configClass != null) {
Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);
if (configClass != enhancedClass) {
if (logger.isTraceEnabled()) {
logger.trace(String.format("Replacing bean definition '%s' existing class '%s' with " +
"enhanced class '%s'", entry.getKey(), configClass.getName(), enhancedClass.getName()));
}
beanDef.setBeanClass(enhancedClass);
}
}
}
catch (Throwable ex) {
throw new IllegalStateException("Cannot load configuration class: " + beanDef.getBeanClassName(), ex);
}
}
}
这里(ConfigurationClassUtils.isFullConfigurationClass(beanDef))会判断有没有标识了FULL的配置类,如果没有下面configBeanDefs
会为空直接返回,如果不为空,对配置类增强,我们锁定这段代码Class<?> enhancedClass = enhancer.enhance(configClass,
this.beanClassLoader);-》Class<?> enhancedClass createClass(newEnhancer(configClass,classLoader));-》
newEnhancer(Class<?> configSuperClass, @Nullable ClassLoader classLoader)最后来到了这里:
在对@Configuration做详细分析时,笔者可以先自行思考,Spring对配置类进行增强之后如何做到采用不同的方式去创建对象,为什么在Spring的容器里都只会存放一份Bean,如果要实现这种功能,是不是需要改变创建对象的方式呢?那么既然我们的工厂我们的@Bean.class是创建对象交给Spring管理,那么如果我们创建对象的方式先通过工厂里面获取,如果获取不到我们再创建对象交给Spring管理是不是就能够实现呢?好了,我想读者应该有些思路了,我们再看一张图
图上大致讲解了配置类转变为增强类后大概是什么样子的,从图上我们可以看到增强代理类有个私有变量,这个变量存放着我们的工厂,那么为什么需要这个工厂我想读者应该有所顿悟。好了我们再回到源码:
如图我们可以大概才到,Spring在这里添加了一个策略,这个策略是声明一个变量名为"$$beanFactory"的公共属性,再回想我们之前添加的接口是不是可以得到工厂,那么我么你现在还差一步,如何将前面的原料加工就是我们代理需要去做的事情:
这个Callback笔者就不分析了,因为涉及到CGLIB的知识,如果读者有情趣可以自行研究。好了我们生成代理后还差一件事情,那就是如何将我们拿到的工厂赋值给代理类得变量中,看下面代码
我们点进去
这里读者是不是豁然开朗,是不是觉得Spring的设计如神一般的存在啊,好了当我们了解到这里其实
invokeBeanFactoryPostProcessors这个方法也差不多结束了,当然还有一些更细节的地方笔者会作为单独的章节进行描述,哎,总算是到了一半了,什么才一半?,毕竟我们真正的对象实例还没开始呢,好的我们在下一小节会对Spring剩余部分进行深度分析。