ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

Spring源码解析之零配置

2022-05-10 01:32:42  阅读:202  来源: 互联网

标签:xml null abd Spring 源码 注解 解析 class metadata


前言

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.他们创建的扫描器都是同一个,扫描注解,注册组件流程是一模一样。

标签:xml,null,abd,Spring,源码,注解,解析,class,metadata
来源: https://www.cnblogs.com/honeylemon/p/16251903.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有