11、Spring源码之@Configuration为什么要被代理放入容器呢

背景:

看源码过程中、 发现被@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多例。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值