背景:
看源码过程中、 发现被@Configuratioin修饰的类会被Spring代理后放入容器内。解决了什么问题?这里记录一下。
知识储备:
cglib、factory-method 、ConfigurationClassBeanPostProcessor、 Spring的生命周期。
代码
@Configuration
public class SpringApp {
public class Bean1 {
private Bean2 bean2;
public void setBean2(Bean2 bean2) {
this.bean2 = bean2;
}
public Bean1(){
System.out.println("Bean1....construcror");
}
}
public class Bean2{
public Bean2(){
System.out.println("Bean2....construcror");
}
}
@Bean
Bean1 bean1(){
Bean1 bean1 = new Bean1();
bean1.setBean2(bean2());
return bean1;
}
@Bean
Bean2 bean2(){
return new Bean2();
}
问题抛出:
上述代码 Spring的 bean2()这个方法会执行几遍?
1、一遍?@Bean标签是被ConfigurationClassBeanPostProcessor解析后的beanDefinition放入Spring容器, 随后Spring会遍历每个BeanDefinition进行实例化始化操作。当然这里实例化方式使用的是factory-method方式。从上面代码看出应该会调用bean1()方法一次 调用bean2()方法两次。
呃呃呃。。。。。逻辑上感觉不应该是一次呢的感觉。
2、二遍? 上面分析了 假如两次 那Bean2 还是单例模式嘛?呃呃呃。。。这个问题就更大了。所以结论是一次。 但是Spring是如何做的呢?看看下面代码分析。
源码跟踪:
1、ConfigurationClassBeanPostProcessor
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
int factoryId = System.identityHashCode(beanFactory);
if (this.factoriesPostProcessed.contains(factoryId)) {
throw new IllegalStateException(
"postProcessBeanFactory already called on this post-processor against " + beanFactory);
}
this.factoriesPostProcessed.add(factoryId);
if (!this.registriesPostProcessed.contains(factoryId)) {
// BeanDefinitionRegistryPostProcessor hook apparently not supported...
// Simply call processConfigurationClasses lazily at this point then.
processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory);
}
enhanceConfigurationClasses(beanFactory);
beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));
}
enhanceConfigurationClasses这个方法 主要做的事情 把@Configuration修饰的类生成代理类返回。
2、开始代理
private Enhancer newEnhancer(Class<?> configSuperClass, @Nullable ClassLoader classLoader) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(configSuperClass);
enhancer.setInterfaces(new Class<?>[] {EnhancedConfiguration.class});
enhancer.setUseFactory(false);
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader));
enhancer.setCallbackFilter(CALLBACK_FILTER);
enhancer.setCallbackTypes(CALLBACK_FILTER.getCallbackTypes());
return enhancer;
}
这里要着重关注一下CALLBACK_FILTER ,包含BeanMethodInterceptor:处理factory-method的回调和BeanFactoryAwareMethodInterceptor:处理setBeanFactory方法的回调。
3、实例化Bean1
public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,
@Nullable Object factoryBean, final Method factoryMethod, Object... args) {
try {
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
ReflectionUtils.makeAccessible(factoryMethod);
return null;
});
}
else {
ReflectionUtils.makeAccessible(factoryMethod);
}
Method priorInvokedFactoryMethod = currentlyInvokedFactoryMethod.get();
try {
currentlyInvokedFactoryMethod.set(factoryMethod);
Object result = factoryMethod.invoke(factoryBean, args);
if (result == null) {
result = new NullBean();
}
return result;
}
finally {
if (priorInvokedFactoryMethod != null) {
currentlyInvokedFactoryMethod.set(priorInvokedFactoryMethod);
}
else {
currentlyInvokedFactoryMethod.remove();
}
}
}
factoryMethod.invoke方法调用动态工厂方法、正好会被BeanMethodInterceptor拦截器拦截。
4、拦截器调用真实方法、实例化Bean1对象。
return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs);
5、拦截器调用真实方法、实例化Bean1对象。bean1方法 这里又调用bean2方法 又进入拦截器
Bean1 bean1(){
Bean1 bean1 = new Bean1();
bean1.setBean2(bean2());
return bean1;
}
6、拦截器又会进行Bean2的实例化并放入容器。并返回Bean2对象。
return resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName);
7、此时Bean1对象实例化完成。 继续实例化Bean2的,Bean2已经存在容器中,直接缓存中获取就OK,Bean2是单例的。
结论:
@Configuratioin修饰的类会被Spring代理后放入容器内为了解决被@Bean修饰的方法,通过代理代理类去调用,这个代理类要要先从容器中获取,容器中找不到,在调用@Bean修饰的方法,保证单例。
说白了就是防止@Bean方法里显式调用其他@Bean修饰的方法、避免被显示调用的@Bean多例。