首页 > 编程语言 > 详细

Spring-IOC(四)

时间:2021-07-29 15:59:48      阅读:23      评论:0      收藏:0      [点我收藏+]

parser.parse

这个方法主要是解析传递进来的bean,这里只会传递一个bean,那就是AppConfig

public void parse(Set<BeanDefinitionHolder> configCandidates) {
	for (BeanDefinitionHolder holder : configCandidates) {
		BeanDefinition bd = holder.getBeanDefinition();
		try {
			if (bd instanceof AnnotatedBeanDefinition) {
				parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
			}
			else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
				parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
			}
			else {
				parse(bd.getBeanClassName(), holder.getBeanName());
			}
		}
		catch (BeanDefinitionStoreException ex) {
			throw ex;
		}
		catch (Throwable ex) {
			throw new BeanDefinitionStoreException(
					"Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
		}
	}

	this.deferredImportSelectorHandler.process();
}

这个方法主要是判断bd属于哪个类型,然后调用对应的parse方法,对于AppConfig对应的类型是AnnotatedBeanDefinition,那么进入第一个parse方法,其实每个parse方法最终调用的都是processConfigurationClass方法

processConfigurationClass

protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
	if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
		return;
	}

	ConfigurationClass existingClass = this.configurationClasses.get(configClass);
	if (existingClass != null) {
		if (configClass.isImported()) {
			if (existingClass.isImported()) {
				existingClass.mergeImportedBy(configClass);
			}
			// Otherwise ignore new imported config class; existing non-imported class overrides it.
			return;
		}
		else {
			// Explicit bean definition found, probably replacing an import.
			// Let‘s remove the old one and go with the new one.
			this.configurationClasses.remove(configClass);
			this.knownSuperclasses.values().removeIf(configClass::equals);
		}
	}

	// Recursively process the configuration class and its superclass hierarchy.
	// 递归处理configuration class和他的父类
	SourceClass sourceClass = asSourceClass(configClass);
	do {
		// 解析配置的类
		sourceClass = doProcessConfigurationClass(configClass, sourceClass);
	}
	while (sourceClass != null);

	this.configurationClasses.put(configClass, configClass);
	System.out.println();
}

这个类看着很简单,其实这个是最复杂的了。主要的逻辑是将传递进来的configClass包装成SourceClass,然后循环调用doProcessConfigurationClass方法,在这个方法进行配置类的解析

doProcessConfigurationClass

protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
			throws IOException {

	// 由于 @Configuration 注解上有 @Component 注解
	if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
		// Recursively process any member (nested) classes first
		// 首先递归处理嵌套类,这个是为了防止在含有@Component的类中还有内部类含有@Component注解
		processMemberClasses(configClass, sourceClass);
	}

	// Process any @PropertySource annotations
	for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
			sourceClass.getMetadata(), PropertySources.class,
			org.springframework.context.annotation.PropertySource.class)) {
		if (this.environment instanceof ConfigurableEnvironment) {
			processPropertySource(propertySource);
		}
		else {
			logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
					"]. Reason: Environment must implement ConfigurableEnvironment");
		}
	}

	// Process any @ComponentScan annotations
	/*
		这里会将@ComponentScan注解中写入的包名下的所有类进行扫描,然后放入beanDefinitionMap中,接着继续调用parse方法就进入了递归
	 */
	Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
			sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
	if (!componentScans.isEmpty() &&
			!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
		for (AnnotationAttributes componentScan : componentScans) {
			// The config class is annotated with @ComponentScan -> perform the scan immediately
			// 此处就是解析@ComponentScan中包名下的所有加了spring注解的类,并放入转换成beanDefinition
			Set<BeanDefinitionHolder> scannedBeanDefinitions =
					this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
			// Check the set of scanned definitions for any further config classes and parse recursively if needed
			for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
				BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
				if (bdCand == null) {
					bdCand = holder.getBeanDefinition();
				}
				// 此处是检查扫描出来的类是否加了@Configuration 注解
				if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
					parse(bdCand.getBeanClassName(), holder.getBeanName());
				}
			}
		}
	}

	// Process any @Import annotations
	processImports(configClass, sourceClass, getImports(sourceClass), true);

	// Process any @ImportResource annotations
	AnnotationAttributes importResource =
			AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
	if (importResource != null) {
		String[] resources = importResource.getStringArray("locations");
		Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
		for (String resource : resources) {
			String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
			configClass.addImportedResource(resolvedResource, readerClass);
		}
	}

	// Process individual @Bean methods
	Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
	for (MethodMetadata methodMetadata : beanMethods) {
		configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
	}

	// Process default methods on interfaces
	processInterfaces(configClass, sourceClass);

	// Process superclass, if any
	if (sourceClass.getMetadata().hasSuperClass()) {
		String superclass = sourceClass.getMetadata().getSuperClassName();
		if (superclass != null && !superclass.startsWith("java") &&
				!this.knownSuperclasses.containsKey(superclass)) {
			this.knownSuperclasses.put(superclass, configClass);
			// Superclass found, return its annotation metadata and recurse
			return sourceClass.getSuperClass();
		}
	}

	// No superclass -> processing is complete
	return null;
}

这个方法主要处理以下几个方面:

  • 处理含有@Component注解
  • 处理含有@PropertySources注解
  • 处理含有@ComponentScans或者@ComponentScan注解
  • 处理含有@Import注解
  • 处理含有@ImportResource注解
  • 处理含有@Bean注解
  • 处理接口默认方法,这个是Java8
  • 处理超类
    处理含有@Component注解
// 由于 @Configuration 注解上有 @Component 注解
if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
	// Recursively process any member (nested) classes first
	// 首先递归处理嵌套类,这个是为了防止在含有@Component的类中还有内部类含有@Component注解
	processMemberClasses(configClass, sourceClass);
}

这个地方如果有内部类并且内部类中也有@Component注解的话,不管是静态内部类还是非静态内部类都会处理

private void processMemberClasses(ConfigurationClass configClass, SourceClass sourceClass) throws IOException {

	Collection<SourceClass> memberClasses = sourceClass.getMemberClasses();
	if (!memberClasses.isEmpty()) {
		// 需要处理的配置类的集合
		List<SourceClass> candidates = new ArrayList<>(memberClasses.size());
		for (SourceClass memberClass : memberClasses) {
			// 如果内部类也是配置类,加入到candidates中
			if (ConfigurationClassUtils.isConfigurationCandidate(memberClass.getMetadata()) &&
					!memberClass.getMetadata().getClassName().equals(configClass.getMetadata().getClassName())) {
				candidates.add(memberClass);
			}
		}
		OrderComparator.sort(candidates);
		for (SourceClass candidate : candidates) {
			if (this.importStack.contains(configClass)) {
				this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
			}
			else {
				this.importStack.push(configClass);
				try {
					processConfigurationClass(candidate.asConfigClass(configClass));
				}
				finally {
					this.importStack.pop();
				}
			}
		}
	}
}
  • 首先就是获取内部类memberClasses集合,如果不存在则跳出该方法
  • 如果存在内部类,则循环遍历该内部类,如果这些内部类也是配置类(是full或者lite类型)则加入到candidates集合中,并对candidates进行排序
  • 循环遍历candidates集合,判断importStack中是否含有外部类,如果存在输出日志
  • 如果不存在,将该外部类存入importStack中,并调用processConfigurationClass方法处理内部类(这里的asConfigClass方法将当前外部配置类的设置到内部配置类的importedBy集合中,表示算作被外部类import引入进来的)
  • 将外部类从importStack中弹出

处理@Component内部类的逻辑说完了,用一个例子来验证一个外部类获取静态和非静态内部类。还是用之前一个接口多个实现类的例子:

@Service
public class ServiceImpl {

	@Resource(name = "conServiceA")
	private MultiService multiService;

	@Override
	public String toString() {
		return "ServiceImpl{" +
				"service=" + multiService +
				‘}‘;
	}

	@Component("conServiceA")
	public static class ConServiceA implements MultiService{}

	@Component("conServiceB")
	public class ConServiceB implements MultiService{}
}

当时这两个内部类都是静态内部类,这里将ConServiceB改为非静态内部类,使用debug启动Main#main方法来看下
技术分享图片
从上图中可以验证之前说的结果

处理含有@PropertySources注解
这个主要是处理配置文件,在启动配置类AppConfig中就有这个注解,再来看下:

@Configuration
@ComponentScan("com.xxx.spring.multiimpl")
@PropertySource("classpath:application.properties")
public class AppConfig {
}

这个类中的PropertySource注解就是在家配置文件,看下配置文件:

za.profile.env=test
za.data=grafana

很简单的两行配置,这两行配置看下是如何解析的:

// Process any @PropertySource annotations
for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
		sourceClass.getMetadata(), PropertySources.class,
		org.springframework.context.annotation.PropertySource.class)) {
	if (this.environment instanceof ConfigurableEnvironment) {
		processPropertySource(propertySource);
	}
	else {
		logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
				"]. Reason: Environment must implement ConfigurableEnvironment");
	}
}
  • 首先判断environment环境是否是ConfigurableEnvironment类型的(在前面介绍过生成environment对象是StandardEnvironment,这个类间接实现了ConfigurableEnvironment接口)
  • 如果是则执行processPropertySource方法
  • 如果不是,打印日志

processPropertySource
这个方法主要是处理配置文件的,获取注解中的属性,对这些属性做处理,找到配置文件的位置,将这些配置文件加载到environment中的MutablePropertySources属性中

private void processPropertySource(AnnotationAttributes propertySource) throws IOException {
	String name = propertySource.getString("name");
	if (!StringUtils.hasLength(name)) {
		name = null;
	}
	String encoding = propertySource.getString("encoding");
	if (!StringUtils.hasLength(encoding)) {
		encoding = null;
	}
	// 解析配置文件的位置
	String[] locations = propertySource.getStringArray("value");
	Assert.isTrue(locations.length > 0, "At least one @PropertySource(value) location is required");
	boolean ignoreResourceNotFound = propertySource.getBoolean("ignoreResourceNotFound");

	Class<? extends PropertySourceFactory> factoryClass = propertySource.getClass("factory");
	PropertySourceFactory factory = (factoryClass == PropertySourceFactory.class ?
			DEFAULT_PROPERTY_SOURCE_FACTORY : BeanUtils.instantiateClass(factoryClass));

	for (String location : locations) {
		try {
			String resolvedLocation = this.environment.resolveRequiredPlaceholders(location);
			// 根据文件位置将文件解析成Resource资源
			Resource resource = this.resourceLoader.getResource(resolvedLocation);
			// 将资源加载到environment中的MutablePropertySources属性中
			addPropertySource(factory.createPropertySource(name, new EncodedResource(resource, encoding)));
		}
		catch (IllegalArgumentException | FileNotFoundException | UnknownHostException ex) {
			// Placeholders not resolvable or resource not found when trying to open it
			if (ignoreResourceNotFound) {
				if (logger.isInfoEnabled()) {
					logger.info("Properties location [" + location + "] not resolvable: " + ex.getMessage());
				}
			}
			else {
				throw ex;
			}
		}
	}
}

private void addPropertySource(PropertySource<?> propertySource) {
	String name = propertySource.getName();
	MutablePropertySources propertySources = ((ConfigurableEnvironment) this.environment).getPropertySources();

	if (this.propertySourceNames.contains(name)) {
		// We‘ve already added a version, we need to extend it
		PropertySource<?> existing = propertySources.get(name);
		if (existing != null) {
			PropertySource<?> newSource = (propertySource instanceof ResourcePropertySource ?
					((ResourcePropertySource) propertySource).withResourceName() : propertySource);
			if (existing instanceof CompositePropertySource) {
				((CompositePropertySource) existing).addFirstPropertySource(newSource);
			}
			else {
				if (existing instanceof ResourcePropertySource) {
					existing = ((ResourcePropertySource) existing).withResourceName();
				}
				CompositePropertySource composite = new CompositePropertySource(name);
				composite.addPropertySource(newSource);
				composite.addPropertySource(existing);
				propertySources.replace(name, composite);
			}
			return;
		}
	}

	if (this.propertySourceNames.isEmpty()) {
		propertySources.addLast(propertySource);
	}
	else {
		String firstProcessed = this.propertySourceNames.get(this.propertySourceNames.size() - 1);
		propertySources.addBefore(firstProcessed, propertySource);
	}
	this.propertySourceNames.add(name);
}

第二个方法看propertySources.addLast(propertySource);将解析好的资源添加到propertySources中,是否还记得之前说environment时有两个:systemEnvironment和systemProperties,这两个是什么时候添加进去的,从这行代码知道自定义资源是添加到systemEnvironment和systemProperties之后的。

还记得在初始化AnnotationConfigApplicationContext类时,要初始化AnnotatedBeanDefinitionReader对象,在AnnotatedBeanDefinitionReader的对应构造方法中有getOrCreateEnvironment方法获取environment,systemEnvironment和systemProperties是在这添加进去的,会调用StandardEnvironment#customizePropertySources

protected void customizePropertySources(MutablePropertySources propertySources) {
	propertySources.addLast(
			new PropertiesPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
	propertySources.addLast(
			new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));
}

根据上面的代码可以看出systemProperties优先于systemEnvironment优先于我们自定义的配置文件。用代码验证下,这里只验证systemEnvironment优先于我们自定义的配置文件这块。
首先在启动的Main.java做如下配置:
技术分享图片
新增一个配置类:

@Configuration
public class ParamConfig {

	@Value("${za.profile.env}")
	private String env;

	public ParamConfig(){
		//System.out.println("param config constructor...");
	}

	public String getEnv() {
		return env;
	}

	public void setEnv(String env) {
		this.env = env;
	}
}

配置文件放在classPath下的application.properties文件中:

za.profile.env=test

Main.java

public class Main {

	public static void main(String[] args) {
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
		ParamConfig config = context.getBean("paramConfig",ParamConfig.class);
		System.out.println(config.getEnv());
	}
}

运行结果如下:

systemEnvironment

从这个运行结果就可以验证上面的结果

处理ComponentScans或者ComponentScan注解
这两个注解就是扫描指定包下的所有符合spring规则的类放入beanDefinitionMap中

Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
		sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
if (!componentScans.isEmpty() &&
		!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
	for (AnnotationAttributes componentScan : componentScans) {
		// The config class is annotated with @ComponentScan -> perform the scan immediately
		// 此处就是解析@ComponentScan中包名下的所有加了spring注解的类,并放入转换成beanDefinition
		Set<BeanDefinitionHolder> scannedBeanDefinitions =
				this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
		// Check the set of scanned definitions for any further config classes and parse recursively if needed
		for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
			BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
			if (bdCand == null) {
				bdCand = holder.getBeanDefinition();
			}
			// 此处是检查扫描出来的类是否加了@Configuration 注解
			if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
				parse(bdCand.getBeanClassName(), holder.getBeanName());
			}
		}
	}
}

循环遍历ComponentScans,针对每一个ComponentScan调用parse方法将componentScan和注解元数据传递进去进行处理

public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {

	ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,
			componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);

	Class<? extends BeanNameGenerator> generatorClass = componentScan.getClass("nameGenerator");

	boolean useInheritedGenerator = (BeanNameGenerator.class == generatorClass);
	scanner.setBeanNameGenerator(useInheritedGenerator ? this.beanNameGenerator :
			BeanUtils.instantiateClass(generatorClass));

	ScopedProxyMode scopedProxyMode = componentScan.getEnum("scopedProxy");
	if (scopedProxyMode != ScopedProxyMode.DEFAULT) {
		scanner.setScopedProxyMode(scopedProxyMode);
	}
	else {
		Class<? extends ScopeMetadataResolver> resolverClass = componentScan.getClass("scopeResolver");
		scanner.setScopeMetadataResolver(BeanUtils.instantiateClass(resolverClass));
	}

	scanner.setResourcePattern(componentScan.getString("resourcePattern"));

	for (AnnotationAttributes filter : componentScan.getAnnotationArray("includeFilters")) {
		for (TypeFilter typeFilter : typeFiltersFor(filter)) {
			scanner.addIncludeFilter(typeFilter);
		}
	}
	for (AnnotationAttributes filter : componentScan.getAnnotationArray("excludeFilters")) {
		for (TypeFilter typeFilter : typeFiltersFor(filter)) {
			scanner.addExcludeFilter(typeFilter);
		}
	}

	boolean lazyInit = componentScan.getBoolean("lazyInit");
	if (lazyInit) {
		scanner.getBeanDefinitionDefaults().setLazyInit(true);
	}

	Set<String> basePackages = new LinkedHashSet<>();
	String[] basePackagesArray = componentScan.getStringArray("basePackages");
	for (String pkg : basePackagesArray) {
		String[] tokenized = StringUtils.tokenizeToStringArray(this.environment.resolvePlaceholders(pkg),
				ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
		Collections.addAll(basePackages, tokenized);
	}

	for (Class<?> clazz : componentScan.getClassArray("basePackageClasses")) {
		basePackages.add(ClassUtils.getPackageName(clazz));
	}

	if (basePackages.isEmpty()) {
		basePackages.add(ClassUtils.getPackageName(declaringClass));
	}

	scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) {
		@Override
		protected boolean matchClassName(String className) {
			return declaringClass.equals(className);
		}
	});
	return scanner.doScan(StringUtils.toStringArray(basePackages));
}

在这个方法中就会看到什么样的注解是符合spring规则,会被放入beanDefinitionMap中进行后续的bean相关操作

  • 创建ClassPathBeanDefinitionScanner对象,创建一个类路径的bean定义扫描器,其中第二个参数是:componentScan.getBoolean("useDefaultFilters"),该参数的含义是是否使用默认的类型过滤器,默认为true,即使用默认类型过滤器,这表示会将@Component、@Named、@ManagedBean,这三个注解的类型过滤器添加到includeFilters集合中。其中@Component及其派生注解都会被扫描到,像@Service、@Controller、@Repository、@Component、@Configuration;具体代码如下:
public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,
		Environment environment, @Nullable ResourceLoader resourceLoader) {

	Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
	this.registry = registry;

	if (useDefaultFilters) {
		registerDefaultFilters();
	}
	setEnvironment(environment);
	setResourceLoader(resourceLoader);
}

protected void registerDefaultFilters() {
	this.includeFilters.add(new AnnotationTypeFilter(Component.class));
	ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
	try {
		this.includeFilters.add(new AnnotationTypeFilter(
				((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));
		logger.trace("JSR-250 ‘javax.annotation.ManagedBean‘ found and supported for component scanning");
	}
	catch (ClassNotFoundException ex) {
		// JSR-250 1.1 API (as included in Java EE 6) not available - simply skip.
	}
	try {
		this.includeFilters.add(new AnnotationTypeFilter(
				((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false));
		logger.trace("JSR-330 ‘javax.inject.Named‘ annotation found and supported for component scanning");
	}
	catch (ClassNotFoundException ex) {
		// JSR-330 API not available - simply skip.
	}
}

看registerDefaultFilters方法中添加默认类型过滤器,添加到includeFilters集合中

  • 获取beanName生成器BeanNameGenerator,默认是AnnotationBeanNameGenerator
  • 生成代理对象的模式ScopedProxyMode,通常是在web中使用
  • 控制符合组件检测的类文件,spring推荐使用includeFilters和excludeFilters
  • 获取Component注解中的includeFilters属性,遍历该属性值添加到ClassPathBeanDefinitionScanner类中的includeFilters属性中
  • 获取Component注解中的excludeFilters属性,遍历该属性值添加到ClassPathBeanDefinitionScanner类中的excludeFilters属性中
  • 获取Component注解中的lazyInit属性,默认为false,如果为true,则将ClassPathBeanDefinitionScanner类中的BeanDefinitionDefaults属性的lazyinit设置为true
  • 获取Component注解中的basePackages属性,解析出该属性中所有的包名添加到basePackages集合中并排序
  • 获取Component注解找那个的basePackageClasses属性,解析出该属性中所有需要扫描的类并添加到basePackages集合中
  • 如果basePackages为空,那么将当前@ComponentScan注解所在的类所属的包路径作为扫描的包路径
  • 调用doScan方法进行扫描

doScan扫描指定包下的类
这个方法就是扫描指定包下的类,将这些符合spring规则的类添加到beanDefinitionMap中

protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
	Assert.notEmpty(basePackages, "At least one base package must be specified");
	Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
	for (String basePackage : basePackages) {
		Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
		for (BeanDefinition candidate : candidates) {
			ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
			candidate.setScope(scopeMetadata.getScopeName());
			String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
			if (candidate instanceof AbstractBeanDefinition) {
				postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
			}
			if (candidate instanceof AnnotatedBeanDefinition) {

				AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
			}
			if (checkCandidate(beanName, candidate)) {
				BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
				definitionHolder =
						AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
				beanDefinitions.add(definitionHolder);
				registerBeanDefinition(definitionHolder, this.registry);
			}
		}
	}
	return beanDefinitions;
}
  • 调用findCandidateComponents方法找出符合要求的类转换成ScannedGenericBeanDefinition类型的bean定义集合
  • 循环遍历这些bean集合
    • 调用resolveScopeMetadata方法解析@Scope注解,为候选 bean 设置代理的方法 ScopedProxyMode
    • 使用 spring 的 beanName 生成器 AnnotationBeanNameGenerator 来查找设置的 beanName 或者生成的 beanName
    • 如果 bean 类型是 AbstractBeanDefinition,那么设置一些默认属性
    • 如果 bean 类型是 AnnotatedBeanDefinition,那么处理类上的其他通用注解:@Lazy,@Primary,@DependsOn,@Role,@Description
    • 调用 checkCandidate,检查给定的 beanName,确定相应的 bean 定义是否需要注册或与现有 bean 定义兼容
    • 如果可以注册,则将 bean 封装成 BeanDefinitionHolder 对象,并放入BeanDefinitionMap 集合中
    • 继续调用 registerBeanDefinition 方法注册 definitionHolder,尝试注册到beanDefinitionMap、beanDefinitionNames、aliasMap 这三个缓存中

findCandidateComponents
该方法在父类ClassPathScanningCandidateComponentProvider中

public Set<BeanDefinition> findCandidateComponents(String basePackage) {

	if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
		return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
	}
	else {
		return scanCandidateComponents(basePackage);
	}
}

从方法中可以看到扫描包路径的方式有两种:

  1. spring5新添加的componentsIndex,该属性通过"META/spring.components"这个组件获取到需要注册的bean定义
  2. 传统包路径扫描方式,一般都是走这种方式
    scanCandidateComponents
    该方法就会将所有符合条件的类
private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
	Set<BeanDefinition> candidates = new LinkedHashSet<>();
	try {
		String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
				resolveBasePackage(basePackage) + ‘/‘ + this.resourcePattern;
		Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
		boolean traceEnabled = logger.isTraceEnabled();
		boolean debugEnabled = logger.isDebugEnabled();
		for (Resource resource : resources) {
			if (traceEnabled) {
				logger.trace("Scanning " + resource);
			}
			if (resource.isReadable()) {
				try {
					MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);

					if (isCandidateComponent(metadataReader)) {
						ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
						sbd.setResource(resource);
						sbd.setSource(resource);

						if (isCandidateComponent(sbd)) {
							if (debugEnabled) {
								logger.debug("Identified candidate component class: " + resource);
							}
							candidates.add(sbd);
						}
						else {
							if (debugEnabled) {
								logger.debug("Ignored because not a concrete top-level class: " + resource);
							}
						}
					}
					else {
						if (traceEnabled) {
							logger.trace("Ignored because not matching any filter: " + resource);
						}
					}
				}
				catch (Throwable ex) {
					throw new BeanDefinitionStoreException(
							"Failed to read candidate component class: " + resource, ex);
				}
			}
			else {
				if (traceEnabled) {
					logger.trace("Ignored because not readable: " + resource);
				}
			}
		}
	}
	catch (IOException ex) {
		throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
	}
	return candidates;
}
  • 将指定包下所有的类转换成Resource资源,后续通过对Resource资源进行一系列操作
  • 使用ASM技术将resource资源解析成MetadataReader对象
  • 调用isCandidateComponent方法判断是否满足typeFilter类型过滤器要求
    • 如果符合,那么将metadataReader包装成ScannedGenericBeanDefinition对象
    • 继续检查给定bean定义是否可以作为候选组件,即是否符合bean定义,如果符合就添加到beanDefinition
      isCandidateComponent
      判断解析出来的MetadataReader对象是否是符合spring规则,即是否符合扫描条件
protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
	for (TypeFilter tf : this.excludeFilters) {
		if (tf.match(metadataReader, getMetadataReaderFactory())) {
			return false;
		}
	}

	for (TypeFilter tf : this.includeFilters) {
		if (tf.match(metadataReader, getMetadataReaderFactory())) {
			return isConditionMatch(metadataReader);
		}
	}
	return false;
}


private boolean isConditionMatch(MetadataReader metadataReader) {
	if (this.conditionEvaluator == null) {
		this.conditionEvaluator =
				new ConditionEvaluator(getRegistry(), this.environment, this.resourcePatternResolver);
	}
	return !this.conditionEvaluator.shouldSkip(metadataReader.getAnnotationMetadata());
}

对于excludeFilters和includeFilters这两个集合上面讲过,excludeFilter是需要排除的,如果解析出来的metadataReader对象和excludeFilter匹配上了,则返回false,即不符合spring扫描条件,不需要加入到bean;如果解析出来的metadataReader对象和includeFilter匹配上还需要调用shouldSkip方法判断@Conditional条件是否满足,如果满足则该对象符合spring扫描条件,添加到beanDefinitionMap中,否则不添加。

isCandidateComponent
这个方法是判断转换成ScannedGenericBeanDefinition对象的bean是否满足bean的定义要求,只有满足了bean的定义要求才能加入到candidates集合中(candidates集合是一个set集合,存放的是BeanDefinition对象)

protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
	AnnotationMetadata metadata = beanDefinition.getMetadata();
	/*
		如果该类是独立的(即它是顶级类或者静态内部类,可以独立于封闭类构造,这一步就把非静态内部类排除了) &&
		(metadata.isConcrete() || (metadata.isAbstract() && metadata.hasAnnotatedMethods(Lookup.class.getName())))
		表示该类是一个具体类或者 该类是一个抽象类并且该类持有 Lookup 注解
		满足以上两个条件,那么就算有资格作为候选组件
	 */
	return (metadata.isIndependent() && (metadata.isConcrete() ||
			(metadata.isAbstract() && metadata.hasAnnotatedMethods(Lookup.class.getName()))));
}

通过上面的注释可以得出:这里扫描的类是独立的,这个独立中的解释是顶级类或者静态内部类,这里只找出静态内部类,而非静态内部类却没有加载出来的,在说第一个注解@Component时,专门扫描的是内部类,包括了静态内部类和非静态内部类。

说完了findCandidateComponents方法,说明找到了所有符合spring扫描条件的对象,并添加到candidates集合中,组成了一个个的BeanDefinition对象,接下来就是对这个集合进行遍历,做一些列的判断,最终将符合条件的放入到beanDefinitionMap中。

resolveScopeMetadata
这个方法主要解析@Scope注解,为候选 bean 设置代理的方法 ScopedProxyMode,之前说过这个一般用在web应用,这里不做解析

generateBeanName
使用spring的beanName生成器为每个bean生成一个name,调用的是org.springframework.context.annotation.AnnotationBeanNameGenerator#generateBeanName

public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
	if (definition instanceof AnnotatedBeanDefinition) {
		String beanName = determineBeanNameFromAnnotation((AnnotatedBeanDefinition) definition);
		if (StringUtils.hasText(beanName)) {
			// Explicit bean name found.
			return beanName;
		}
	}
	// Fallback: generate a unique default bean name.
	return buildDefaultBeanName(definition, registry);
}

这个生成beanName的规则是:

  1. 首先调用determineBeanNameFromAnnotation方法从注解中查找设置的name
  2. 如果找不到就调用buildDefaultBeanName方法使用spring自己的规则生成beanName

postProcessBeanDefinition
如果是AbstractBeanDefinition类型,则调用postProcessBeanDefinition,使用AbstractBeanDefinition的一些默认属性

	protected void postProcessBeanDefinition(AbstractBeanDefinition beanDefinition, String beanName) {
		beanDefinition.applyDefaults(this.beanDefinitionDefaults);
		if (this.autowireCandidatePatterns != null) {
			beanDefinition.setAutowireCandidate(PatternMatchUtils.simpleMatch(this.autowireCandidatePatterns, beanName));
		}
	}

	public void applyDefaults(BeanDefinitionDefaults defaults) {
		setLazyInit(defaults.isLazyInit());
		setAutowireMode(defaults.getAutowireMode());
		setDependencyCheck(defaults.getDependencyCheck());
		setInitMethodName(defaults.getInitMethodName());
		setEnforceInitMethod(false);
		setDestroyMethodName(defaults.getDestroyMethodName());
		setEnforceDestroyMethod(false);
	}

processCommonDefinitionAnnotations
如果是AnnotatedBeanDefinition类型,则调用processCommonDefinitionAnnotations方法处理一些通用注解:@Lazy,@Primary,@DependsOn,@Role,@Description。从上面的方法中可以得出扫描出来符合条件的对象会被包装成ScannedGenericBeanDefinition对象然后放入candidates集合中,ScannedGenericBeanDefinition是AnnotatedBeanDefinition实现类

static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd, AnnotatedTypeMetadata metadata) {
	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);
	}
	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"));
	}
}

checkCandidate
校验重复bean

protected boolean checkCandidate(String beanName, BeanDefinition beanDefinition) throws IllegalStateException {
    if (!this.registry.containsBeanDefinition(beanName)) {
        return true;
    }
    BeanDefinition existingDef = this.registry.getBeanDefinition(beanName);
    BeanDefinition originatingDef = existingDef.getOriginatingBeanDefinition();
    if (originatingDef != null) {
        existingDef = originatingDef;
    }
    if (isCompatible(beanDefinition, existingDef)) {
        return false;
    }
    throw new ConflictingBeanDefinitionException("Annotation-specified bean name ‘" + beanName +
            "‘ for bean class [" + beanDefinition.getBeanClassName() + "] conflicts with existing, " +
            "non-compatible bean definition of same name and class [" + existingDef.getBeanClassName() + "]");
}

校验这个bean是否已经存在了,如果不存在返回true,说明可以将这个bean放入beanDefinitionMap中,如果存在需要校验已经存在bean和传递进来的bean是否兼容,如果兼容返回false,不需要再注册了;如果不兼容则抛出异常。
这里也就是使用@Component等组件注解标注bean的不能有重名的逻辑了
到这里就把ConfigurationClassParser#doProcessConfigurationClass方法中关于@ComponentScan或者@ComponentScans注解中的componentScanParser.parse介绍完了。这个方法介绍完,那么存在scannedBeanDefinitions集合中的beanDefinitionHolder都是符合spring条件的bean,从这些BeanDefinitionHolder中获取对应的BeanDefinition,然后调用ConfigurationClassUtils.checkConfigurationClassCandidate方法判断这些符合条件的bean中是否含有full或者lite中的注解,如果存在的话,递归调用parse方法再次进入doProcessConfigurationClass方法进行解析

处理含有@Import注解的bean

processImports(configClass, sourceClass, getImports(sourceClass), true);

@Import这是一个非常实用的注解,像我们平时用的@FeignClient注解,如果要使用这个注解需要使用@EnableFeignClients注解开启这个功能,而@EnableFeignClients注解中就有Import这个注解;还有使用AspectJ开启AOP功能,需要使用@EnableAspectJAutoProxy注解,而在这个注解中也有@Import这个注解的影子。上面这行代码就是处理@Import注解的,具体代码逻辑如下:

private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
        Collection<SourceClass> importCandidates, boolean checkForCircularImports) {

    if (importCandidates.isEmpty()) {
        return;
    }

    if (checkForCircularImports && isChainedImportOnStack(configClass)) {
        this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
    }
    else {
        this.importStack.push(configClass);
        try {
            for (SourceClass candidate : importCandidates) {

                if (candidate.isAssignable(ImportSelector.class)) {
                    // Candidate class is an ImportSelector -> delegate to it to determine imports
                    Class<?> candidateClass = candidate.loadClass();
                    ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
                    ParserStrategyUtils.invokeAwareMethods(
                            selector, this.environment, this.resourceLoader, this.registry);
                    if (selector instanceof DeferredImportSelector) {
                        this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
                    }
                    else {
                        String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
                        Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
                        processImports(configClass, currentSourceClass, importSourceClasses, false);
                    }
                }

                else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
                    // Candidate class is an ImportBeanDefinitionRegistrar ->
                    // delegate to it to register additional bean definitions
                    Class<?> candidateClass = candidate.loadClass();
                    ImportBeanDefinitionRegistrar registrar =
                            BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
                    ParserStrategyUtils.invokeAwareMethods(
                            registrar, this.environment, this.resourceLoader, this.registry);
                    configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
                }
                else {
                    // Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
                    // process it as an @Configuration class
                    this.importStack.registerImport(
                            currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
                    processConfigurationClass(candidate.asConfigClass(configClass));
                }
            }
        }
        catch (BeanDefinitionStoreException ex) {
            throw ex;
        }
        catch (Throwable ex) {
            throw new BeanDefinitionStoreException(
                    "Failed to process import candidates for configuration class [" +
                    configClass.getMetadata().getClassName() + "]", ex);
        }
        finally {
            this.importStack.pop();
        }
    }
}

这里需要声明一点:使用@Import注解引入的class本身不会在该方法中被加入到beanDefinitionMap中,这里只会加入到相应的缓存中,后续会在loadBeanDefinition方法中加入到beanDefinitionMap中

从上面的方法中可以得出以下结论:
循环遍历importCandidates集合,判断每个SourceClass是否是如下几个类型:

  1. 是否是ImportSelector类型
  2. 是否是ImportBeanDefinitionRegistrar类型
  3. 是否是普通类型(如果以上两种都不是,那么肯定是第三种,因为在for循环之前判断了该SourceClass中是否含有Import注解)

如果是ImportSelector类型:该class本身不会被注册为beanDefinition,但是它的selectImports方法返回的类路径的数组中类可能会被注册为beanDefinition,在该方法中会循环调用processImports方法处理selectImports方法返回的类路径中的类,直到遇到是ImportBeanDefinitionRegistrar类型或者是一个普通类

如果是ImportBeanDefinitionRegistrar类型:该class本身不会被注册为beanDefinition,但是它的registerBeanDefinitions方法可用于注册为beanDefinition,在该方法中仅仅是加入到importBeanDefinitionRegistrars集合中,等待后续loadBeanDefinition方法进行处理

如果是普通class类型:该class会尝试加入到beanDefinition中,但在该方法中只会调用processConfigurationClass方法处理并加入到configurationClasses缓存中,等待后续的loadBeanDefinition方法进行处理

处理@ImportResource注解
该注解主要是为了引入xml文件配置,如果你的项目是基于注解开发,但又有xml配置,此时可以使用该注解处理

*处理@Bean注解
这里是处理方法上含有@Bean注解,在该方法中同样是不会被注册为beanDefinition,仅仅是加入到beanMethods集合中,等待后续的处理

Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
	configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}

处理接口上的默认方法
这个是Java8之后的新特定,主要是处理接口上含有@Bean注解的方法,处理方式同上,仅仅是加入到beanMethods集合中等待后续处理

处理超类
配置类的父类同样会被解析,但是已"java"开头的类路径的父类不会被处理,比如Object类这样rt.jar中类
如果存在父类并且不是以"java"开头的并且在knownSuperclasses集合中不存在,那么将该类加入到knownSuperclasses集合中,并且返回父类。返回之后会继续调用processConfigurationClass方法进入到下一次的循环处理这个父类

if (sourceClass.getMetadata().hasSuperClass()) {
    String superclass = sourceClass.getMetadata().getSuperClassName();
    if (superclass != null && !superclass.startsWith("java") &&
            !this.knownSuperclasses.containsKey(superclass)) {
        this.knownSuperclasses.put(superclass, configClass);
        // Superclass found, return its annotation metadata and recurse
        return sourceClass.getSuperClass();
    }
}

以上就是处理所有对应的注解,在processConfigurationClass方法中有一个do while循环,终止条件是sourceClass为null,即一直处理直至返回null。

上面这些处理注解的是ConfigurationClassPostProcessor#processConfigBeanDefinitions方法中ConfigurationClassParser#parse方法处理的步骤。介绍到这是不是忘了前面说的什么了,这个方法就是如此的复杂。。

Spring-IOC(四)

原文:https://www.cnblogs.com/yashon/p/15046037.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!