Spring源码解析之零配置

前言

1.spring在配置时代,都是基于xml配置的,启动一个spring容器,通过ClassPathXmlApplicationContext类的refresh方法,然后解析bean标签,完成bean的注册,解析自定义标签<context:component-scan base-package=cn.com.dq.xml></context:component-scan>对注解的支撑,完成bean的注册
2.spring在零配置时代,基于注解,如何启动容器?如何完成对注解的支撑?

AnnotationConfigApplicationContext

AnnotationConfigApplicationContext是基于注解启动spring容器的类,它有2个构造方法,1.参数是包路径,2.参数是一个类

构造方法参数是包路径

public AnnotationConfigApplicationContext(String... basePackages) { 		this(); 		scan(basePackages); 		refresh(); 	}

this方法

public AnnotationConfigApplicationContext() { 		this.reader = new AnnotatedBeanDefinitionReader(this); 		this.scanner = new ClassPathBeanDefinitionScanner(this); 	}

this方法是他的无参构造器,在无参构造器中,干了2件事情,1.new了一个AnnotatedBeanDefinitionReader,2.new 了一个ClassPathBeanDefinitionScanner

  • AnnotatedBeanDefinitionReader看着似曾相识,我们在xml启动容器的时候,要做xml解析,创建了一个XmlBeanDefinitionReader
  • ClassPathBeanDefinitionScanner也是看着似曾相识,我们在xml启动容器时候,xml自定义标签解析<context:component-scan base-package=cn.com.dq.xml></context:component-scan>标签时候,创建了一个扫描器就是ClassPathBeanDefinitionScanner

scan方法

public void scan(String... basePackages) { 		Assert.notEmpty(basePackages, At least one base package must be specified); 		this.scanner.scan(basePackages); 	} 	 public int scan(String... basePackages) { 		int beanCountAtScanStart = this.registry.getBeanDefinitionCount();  		doScan(basePackages);  		// Register annotation config processors, if necessary. 		if (this.includeAnnotationConfig) { 			AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry); 		}  		return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart); 	}

看到上面的代码又是似曾相识,我们又会联想到xml的方式
我们看xml方式下component-scan扫描的源码

public BeanDefinition parse(Element element, ParserContext parserContext) { 		String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE); 		basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage); 		String[] basePackages = StringUtils.tokenizeToStringArray(basePackage, 				ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);  		// Actually scan for bean definitions and register them. 		ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element); 		Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages); 		registerComponents(parserContext.getReaderContext(), beanDefinitions, element);  		return null; 	}

我们可以看到doScan是同一个方法,因为他俩的扫描器是同一个类
我们在看xml方式下组件注册registerComponents(parserContext.getReaderContext(), beanDefinitions, element);
最终调到的是AnnotationConfigUtils.registerAnnotationConfigProcessors(readerContext.getRegistry(), source);
而基于注解的方式小组件注册AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);跟下去调的是
registerAnnotationConfigProcessors(registry, null);发现又是同一个方法,只是基于注解的第二个参数null而已

refresh方法

发现与xml方式的又是同一个方法,因为AnnotationConfigApplicationContext与ClassPathXmlApplicationContext的父类是AbstractApplicationContext,refresh方法是AbstractApplicationContext里面的方法

构造方法参数是类

public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) { 		this(); 		register(annotatedClasses); 		refresh(); 	}

他与构造器参数是包路径唯一不同的是register方法,我们来看register方法

register

void doRegisterBean(Class<T> annotatedClass, @Nullable Supplier<T> instanceSupplier, @Nullable String name, 			@Nullable Class<? extends Annotation>[] qualifiers, BeanDefinitionCustomizer... definitionCustomizers) {  		AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass); 		if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) { 			return; 		}  		abd.setInstanceSupplier(instanceSupplier); 		ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd); 		abd.setScope(scopeMetadata.getScopeName()); 		String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));  		AnnotationConfigUtils.processCommonDefinitionAnnotations(abd); 		if (qualifiers != null) { 			for (Class<? extends Annotation> qualifier : qualifiers) { 				if (Primary.class == qualifier) { 					abd.setPrimary(true); 				} 				else if (Lazy.class == qualifier) { 					abd.setLazyInit(true); 				} 				else { 					abd.addQualifier(new AutowireCandidateQualifier(qualifier)); 				} 			} 		} 		for (BeanDefinitionCustomizer customizer : definitionCustomizers) { 			customizer.customize(abd); 		}  		BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName); 		definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); 		BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry); 	}

1.传入的参数class封装成beanDefinition

AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass); 

2.判断是否存在@Condition注解,如果有,会判断该类上面是否有@Configuration,@Component,@ComponentScan,@Import,@ImportResource,如果都没有,然后判断方法上是否有@Bean注解
对于有其中任何一个注解,代码就不会往下面执行,为什么?因为@Condition是会进行条件判断。springboot中会大量运用该注解,我们这里是没有这个注解的,代码继续往下

public boolean shouldSkip(@Nullable AnnotatedTypeMetadata metadata, @Nullable ConfigurationPhase phase) { 		if (metadata == null || !metadata.isAnnotated(Conditional.class.getName())) { 			return false; 		}  		if (phase == null) { 			if (metadata instanceof AnnotationMetadata && 					ConfigurationClassUtils.isConfigurationCandidate((AnnotationMetadata) metadata)) { 				return shouldSkip(metadata, ConfigurationPhase.PARSE_CONFIGURATION); 			} 			return shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN); 		} 		 		public static boolean isConfigurationCandidate(AnnotationMetadata metadata) { 		return (isFullConfigurationCandidate(metadata) || isLiteConfigurationCandidate(metadata)); 	} 	 	public static boolean isFullConfigurationCandidate(AnnotationMetadata metadata) { 		return metadata.isAnnotated(Configuration.class.getName()); 	} 	 	public static boolean isLiteConfigurationCandidate(AnnotationMetadata metadata) { 		// Do not consider an interface or an annotation... 		if (metadata.isInterface()) { 			return false; 		}  		// Any of the typical annotations found? 		for (String indicator : candidateIndicators) { 			if (metadata.isAnnotated(indicator)) { 				return true; 			} 		}  		// Finally, let's look for @Bean methods... 		try { 			return metadata.hasAnnotatedMethods(Bean.class.getName()); 		} 		catch (Throwable ex) { 			if (logger.isDebugEnabled()) { 				logger.debug(Failed to introspect @Bean methods on class [ + metadata.getClassName() + ]:  + ex); 			} 			return false; 		} 	}

3.接着就是对beanDefinition的scope属性赋值,接着执行了这行代码
AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);,点进去看

static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd, AnnotatedTypeMetadata metadata) { 		//对@Lazy注解支持 		AnnotationAttributes lazy = attributesFor(metadata, Lazy.class); 		if (lazy != null) { 			abd.setLazyInit(lazy.getBoolean(value)); 		} 		else if (abd.getMetadata() != metadata) { 			lazy = attributesFor(abd.getMetadata(), Lazy.class); 			if (lazy != null) { 				abd.setLazyInit(lazy.getBoolean(value)); 			} 		}  		if (metadata.isAnnotated(Primary.class.getName())) { 			abd.setPrimary(true); 		} 		//对@DependsOn注解支持 		AnnotationAttributes dependsOn = attributesFor(metadata, DependsOn.class); 		if (dependsOn != null) { 			abd.setDependsOn(dependsOn.getStringArray(value)); 		}  		AnnotationAttributes role = attributesFor(metadata, Role.class); 		if (role != null) { 			abd.setRole(role.getNumber(value).intValue()); 		} 		AnnotationAttributes description = attributesFor(metadata, Description.class); 		if (description != null) { 			abd.setDescription(description.getString(value)); 		} 	}

发现和xml的又一样,就是对于一些注解的支撑,然后填充beanDefinition中的属性,包含了@Lazy,@DependsOn等注解的支撑。
4.接着就是这行代码BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);看方法名是注册bean

public static void registerBeanDefinition( 			BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) 			throws BeanDefinitionStoreException {  		// Register bean definition under primary name. 		String beanName = definitionHolder.getBeanName();  		//完成BeanDefinition的注册 		registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());  		//建立别名和 id的映射,这样就可以根据别名获取到id 		// Register aliases for bean name, if any. 		String[] aliases = definitionHolder.getAliases(); 		if (aliases != null) { 			for (String alias : aliases) { 				registry.registerAlias(beanName, alias); 			} 		} 	}

上述代码又回到了xml注册bean的方法,构造方法参数是类是,是将指定的类注册到spring容器中,并实例化。

总结

基于xml和基于注解bean的注册的相同与不同
不同点:
1.基于xml创建的解析器是XmlBeanDefinitionReader,而基于注解的是AnnotatedBeanDefinitionReader
2.基于xml他是解析component-scan标签创建扫描器,而基于注解的是直接new的扫描器
相同点:
1.他们创建的扫描器都是同一个,扫描注解,注册组件流程是一模一样。