Spring配置类源码解析(上)

开发者在使用Spring的时候往往会以为加了@Configuration注解的类可以被称之为配置类,但是在Spring中,配置类的定义如下:

      1.加了@Configuration注解,并且proxyBeanMethods属性为true的Bean,被称为Full配置类

      2.加了@Configuration注解,但是proxyBeanMethods属性不为true的Bean,被称为Lite配置类

      3.加了@Component、@ComponentScan、@Import、@ImportResource四个注解的Bean,被称为Lite配置类

      4.如果一个Bean,如上都不满足,但是在其方法上存在@Bean注解的,也被称为Lite配置类

        1.生成配置类的源码如下(Spring的启动源码太长了,本文只讲解关键点)

                主要方法:ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)        

		Map<String, Object> config = metadata.getAnnotationAttributes(Configuration.class.getName());

		// 存在@Configuration,并且proxyBeanMethods不为false(为true或为null)时,就是Full配置类
		if (config != null && !Boolean.FALSE.equals(config.get("proxyBeanMethods"))) {
			beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
		}
		// 存在@Configuration,并且proxyBeanMethods为false时,是lite配置类
		// 或者不存在@Configuration,但是只要存在@Component、@ComponentScan、@Import、@ImportResource四个中的一个,就是lite配置类
		// 或者不存在@Configuration,只要存在@Bean注解了的方法,就是lite配置类
		else if (config != null || isConfigurationCandidate(metadata)) {
			beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
		}
	public static boolean isConfigurationCandidate(AnnotationMetadata metadata) {
		// Do not consider an interface or an annotation...
		if (metadata.isInterface()) {
			return false;
		}

		// Any of the typical annotations found?
		// 只要存在@Component、@ComponentScan、@Import、@ImportResource四个中的一个,就是lite配置类
		for (String indicator : candidateIndicators) {
			if (metadata.isAnnotated(indicator)) {
				return true;
			}
		}

		// Finally, let's look for @Bean methods...
		// 只要存在@Bean注解了的方法,就是lite配置类
		return hasBeanMethods(metadata);
	}

        通过分析上面两段代码就能得出最开始的那四条结论,不过还要补充一点的就是,BeanFactoryPostProcessor.class || BeanPostProcessor.class || AopInfrastructureBean.class || EventListenerFactory.class,如果当前正在生成BeanDefinition中的beanClass属性是实现了这四个接口的,那么一律不能当做配置类

        2.配置类的解析(ConfigurationClass->就是配置类)

               Spring在启动的时候会往里面注册一个ConfigurationClassPostProcessor,然后因为实现了BeanDefinitionRegistryPostProcessor接口,所以会去调用postProcessBeanDefinitionRegistry()方法

protected final void parse(Class<?> clazz, String beanName) throws IOException {
   processConfigurationClass(new ConfigurationClass(clazz, beanName), DEFAULT_EXCLUSION_FILTER);
}

                在其对应的解析器ConfigurationClassParser中,其parse方法下的doProcessConfigurationClass()方法就实现了主要的解析逻辑,可以挑选里面部分代码阅读其逻辑

        @Component:会去解析其内部类是否是lite配置类,然后进行递归调用,直到最后全部解析完成

	private void processMemberClasses(ConfigurationClass configClass, SourceClass sourceClass,
			Predicate<String> filter) throws IOException {
        //获取该类的内部类
		Collection<SourceClass> memberClasses = sourceClass.getMemberClasses();
		if (!memberClasses.isEmpty()) {
			List<SourceClass> candidates = new ArrayList<>(memberClasses.size());
			for (SourceClass memberClass : memberClasses) {
				// 内部类是不是lite配置类
				if (ConfigurationClassUtils.isConfigurationCandidate(memberClass.getMetadata()) &&
						!memberClass.getMetadata().getClassName().equals(configClass.getMetadata().getClassName())) {
					candidates.add(memberClass);
				}
			}
		}
	}

       @ComponentScan:ComponentScan很特殊,他是这些注解里面唯一一个会去注册BeanDefinition的,先遍历获取到的@ComponentScan注解,然后根据调用其parse方法进行一个,parse方法的最后会调用Scanner.scan()方法扫描,然后进行BeanDefinition的注册,注册逻辑在(Spring源码解析)中有体现

			for (AnnotationAttributes componentScan : componentScans) {
				// 这里就会进行扫描,得到的BeanDefinition会注册到Spring容器中
				Set<BeanDefinitionHolder> scannedBeanDefinitions =
						this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());

				for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
					BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
					if (bdCand == null) {
						bdCand = holder.getBeanDefinition();
					}
					// 检查扫描出来的BeanDefinition是不是配置类(full和lite)
					if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
						parse(bdCand.getBeanClassName(), holder.getBeanName());
					}
				}
			}
parse{
    return scanner.doScan(StringUtils.toStringArray(basePackages));
}

              @Import (这边放入的是部分源码,我做了截取):第一步是先判断是否实现了ImportSelector接口,如果实现了,那么强转然后再判断是否是延迟的ImportSelector,如果是,则缓存到list中,如果不是,那么立马执行对应的selectImports方法返回字串,然后根据字串生成对应的类,然后递归解析新生成的类中是否存在@Import

	private void processImports() {

		//互相Import则抛错
		if (checkForCircularImports && isChainedImportOnStack(configClass)) {
			this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
		} else {
			this.importStack.push(configClass);
			//如果实现了ImportSelector,那么分两种情况,一种是DeferredImportSelector,另一种则是正常的
			if (candidate.isAssignable(ImportSelector.class)) {
				//强转转成ImportSelector
				ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,
						this.environment, this.resourceLoader, this.registry);
				//这种延迟的,内部会直接放到一个list中,等后续在处理
				if (selector instanceof DeferredImportSelector) {
					this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
				} else {
					// 如果import的是普通的ImportSelector,那么直接调用其方法返回一个字串,然后解析这些字串
					String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
					// 继续处理selectImports()所返回的类,如果还有这种类型的,那么继续递归调用
					Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
					processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
				}
			}
		}
		// 如果import的类实现了ImportBeanDefinitionRegistrar接口
		else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
			Class<?> candidateClass = candidate.loadClass();
			ImportBeanDefinitionRegistrar registrar = ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class, this.environment, this.resourceLoader, this.registry);
			//缓存到importBeanDefinitionRegistrars中,并不会立马执行
			configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
		}
		// 如果import的类就是普通的类
		else {
			this.importStack.registerImport(currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
			// asConfigClass会为@Import进来的Class其生成对应的ConfigurationClass,并且记录它是谁@Import的
			processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
		}
	}

1. 如果导入的是普通ImportSelector,那么会调用selectImports方法将返回的字符串生成类再次调用processImports()

2.如果导入的是特殊ImportSelector,DeferredImportSelector,那么缓存到(ConfigClass.deferredImportSelectors)这个属性中,然后会在最外层的ConfigurationClassParser.parse(Set<BeanDefinitionHolder> configCandidates)方法,也就是当这一轮的所有配置类(在配置类的解析过程中,可能会有新的配置类生成)解析完成之后,最后去执行对应的selectImports方法,也就是1的逻辑

3.如果导入的是普通类,那么会直接把它包装成一个ConfigurationClass,然后再去执行一遍配置类的处理逻辑(就是@Component,@ComponentScan....这些注解的处理逻辑,并且把它加到configurationClasses中),然后再去执行processConfigurationClass(这里是递归,他这里就会先解析导入的类下面有没有要扫描并且生成的配置类,这边要小心)

4.如果导入的是ImportBeanDefinitionRegistrar,那么缓存到(ConfigClass.importBeanDefinitionRegistrars)这个属性中,在这一轮所有的ConfigurationClass生成完之后,再去调用其中的方法registerBeanDefinitions注册BeanDefinition

        这三种注解是解析配置类的主要逻辑,其他的注解(比如@ImportResource,类中有@Bean的方法)是如何生成对应的ConfigurationClass的,读者可以自行解读一下后续篇章会讲解除了@ComponentScan之外,其他注解生成ConfigurationClass之后后续是如何生成BeanDefinition的(包括@import的第四步到底是如何执行的),并且@Bean对应的逻辑又该如何执行

总结:

        @Component:检查内部类是否是配置类

        @ComponentScan:扫描路径并且注册BeanDefinition,并且检查是否存在配置类,递归解析新配置类

        @Import:processImports方法是核心逻辑

                1.如果只实现了普通的ImportSelector接口,那么对返回的字符串进行类生成然后继续递归解析(执行processImports方法)

                2.如果实现了DeferredImportSelector接口,那么缓存到deferredImportSelectors中,等这一轮配置类(在配置类的解析过程中,可能会有新的配置类生成)最后在执行selectImports方法

                3.如果是普通类型,则直接生成ConfigurationClass并且去优先处理导入的这个类的配置类的处理逻辑,然后存放到configurationClasses中

                4.如果导入的是ImportBeanDefinitionRegistrar,那么缓存到(ConfigClass.importBeanDefinitionRegistrars)这个属性中,到这一轮配置类解析完之后,再去调用其registerBeanDefinitions注册生成BeanDefinition

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值