Spring之 @Configuration 解析

概述

在 spring 进行 执行refresh() 的invokeBeanFactoryPostProcessors(beanFactory),其中最重要的是执行ConfigurationClassPostProcessor 的postProcessBeanDefinitionRegistry()和 postProcessBeanFactory()这两个方法
在 postProcessBeanDefinitionRegistry()中,会进行类的扫描,然后生成对应的 beanDefinition,在这个过程会对配置类进行判断,如果加了@Configuration 机会将这个类元数据的属性标记为full ,反之标记为lite
至于这个变量真正的操作的方法并不在postProcessBeanDefinitionRegistry()中,而是在postProcessBeanFactory()里面

postProcessBeanFactory()

@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
		int factoryId = System.identityHashCode(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(beanFactory) 判断这个BeanFactory中的配置类是否需要生成代理对象

public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {
		Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<>();
		for (String beanName : beanFactory.getBeanDefinitionNames()) {
			BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
			Object configClassAttr = beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE);
			MethodMetadata methodMetadata = null;
			if (beanDef instanceof AnnotatedBeanDefinition) {
				methodMetadata = ((AnnotatedBeanDefinition) beanDef).getFactoryMethodMetadata();
			}
			if ((configClassAttr != null || methodMetadata != null) && beanDef instanceof AbstractBeanDefinition) {
	
				AbstractBeanDefinition abd = (AbstractBeanDefinition) beanDef;
				if (!abd.hasBeanClass()) {
					...//省略try catch
						abd.resolveBeanClass(this.beanClassLoader);
					...
				}
			}

			// 被@Configuration标注的配置类就是full
			if (ConfigurationClassUtils.CONFIGURATION_CLASS_FULL.equals(configClassAttr)) {
				....//省略
				configBeanDefs.put(beanName, (AbstractBeanDefinition) beanDef);
			}
		}
		if (configBeanDefs.isEmpty()) {
			// nothing to enhance -> return immediately
			return;
		}

		// 生成AppConfig的代理对象
		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);
			// Set enhanced subclass of the user-specified bean class
			Class<?> configClass = beanDef.getBeanClass();
			// 增强类
			Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);
			if (configClass != enhancedClass) {
				....
				// 将增强类设置给beanDefinition,后续基于BeanDefinition产生的bean就是增加类的对象了
				beanDef.setBeanClass(enhancedClass);
			}
		}
	}

首先来看这段代码的第一个 for 循环:
beanFactory.getBeanDefinitionNames():从beanFactory中拿出所以的beanDefinition,然后循环遍历
其中一个非常重要的判断就是判断这个类的是不是标注了 full,如果标注了就说明是一个全配置类,然后将其加入到
configBeanDefs 这个集合中
第二个for循环:
就是对 configBeanDefs 这个集合进行处理,会对这里面的集合生成代理对象,然后将这个代理对象的类型设置到BeanDefinition的
beanClass属性里面
这就说明,之后spring 在对这个配置类里面标注了@Bean的方法getBean()的时候就会进入,这个代理对象的增强逻辑

下面直接来看 spring 对这个配置类道理进行了什么样的增强

intercept

public Object intercept(Object enhancedConfigInstance, Method beanMethod, Object[] beanMethodArgs,
					MethodProxy cglibMethodProxy) throws Throwable {

			// 得到bean工厂和当前正在调用的beanMethod对应的beanName
			ConfigurableBeanFactory beanFactory = getBeanFactory(enhancedConfigInstance);
			String beanName = BeanAnnotationHelper.determineBeanNameFor(beanMethod);

			if (BeanAnnotationHelper.isScopedProxy(beanMethod)) {
				String scopedBeanName = ScopedProxyCreator.getTargetBeanName(beanName);
				if (beanFactory.isCurrentlyInCreation(scopedBeanName)) {
					beanName = scopedBeanName;
				}
			}
		//	省略部分代码
			if (isCurrentlyInvokedFactoryMethod(beanMethod)) {
				...//省略日志
				// invokeSuper 执行原本的逻辑
				return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs);
			}
			return resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName);
		}

为了方便讲解这里举个例子

@Configuration
public class AppConfig(
	@Bean
	public Mapper mapper(){
		return new Mapper();
	}
	@Bean
	public Server server(){
		Mapper mapper = mapper();
		return new Server(mapper);
	}
)

这个例子就是Server 需要进行构造方法注入Mapper 对象,那么加了@Confguration 和没加 @Confguration会有什么区别呢?
加上@Confguration 获取的 Server 对象中的Mapper==Mapper对象
没加上@Confguration 获取的 Server 对象中的Mapper != Mapper对象

这里没加上 @Confguration 其实是比较好理解的,因为这个类的两个方法很显然是new 了两个 Mapper 对象

对于加上了@Confguration 我们结合代码进行分析
先是得到bean工厂和当前正在调用的beanMethod对应的beanName,
然后关键的判断的是 isCurrentlyInvokedFactoryMethod(beanMethod)
这个判断就是 如果现在真在实例化的是 Server ,但是在Server里面会执行 mapper(),那么这里会返回false
如果是实例化的是 Mpper ,那么这里返回的就是true

当返回true的时候就回去执行cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs),这里就是去invoke 父类的逻辑,也就是原本的mapper的逻辑
当返回false 的时候resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName),在这个方法里面有一行很关键的代码

Object beanInstance = (useArgs ? beanFactory.getBean(beanName, beanMethodArgs) :
						beanFactory.getBean(beanName));

也就是说,当spring 实例化Server 对象的时候,会从spring 工厂里面去拿,那么即然是从spring的工厂里面去拿,如果这个Mapper是单例的,那么拿到的肯定就是同一个 Mapper 了

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值