我是靠谱客的博主 重要小白菜,最近开发中收集的这篇文章主要介绍第四章:@ComponentScan注解源码的分析,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

我的源码地址:https://github.com/chenchaofan1995/spring

看@ComponentScan注解解析的源码时,一般都要明确下面几个问题?(末尾有测试代码。)

1:那个接口对@ComponentScan进行解析?
答:ComponentScanAnnotationParser类的parse接口。

2:该接口的构造函数都需要些什么参数,并这些参数都是干什么的?
答:ComponentScanAnnotationParser的构造函数需要如下参数。
a:BeanDefinitionRegistry接口:该接口是将扫描到的bean定义添加进bean定义容器中。。
b:BeanNameGenerator接口:扫描到的bean定义是需要bean名称。
c:ResourceLoader接口:该接口是资源加载器。主要获取包下的资源。
d:Environment接口:扫描到的bean定义可能需要一些配置属性,而配置属性统一存储Environment对象里。

3:parse() 接口需要哪些参数?
答:需要@ComponentScan的注解属性值和被@ComponentScan注解标记的类名。

下面看看parse()接口内部逻辑。(配置代码➕断点查看)

第一步:创建扫描包下bean的扫描器(scanner)。

第二步:从@ComponentScan的注解属性里获取属性值,并赋值给scanner扫描器。

第三步:调用scanner.doScan()接口,开始真正的扫描,描包下的bean,并返回BeanDefinition。

public class ComponentScanAnnotationParser {
	/**
	 *该对象存储所有的环境变量
	*/
	private final Environment environment;
	/**
	 * 资源加载器
	*/
	private final ResourceLoader resourceLoader;
	/**
	* bean的名称生成器
	*/
	private final BeanNameGenerator beanNameGenerator;
	/**
	* 该对象具有bean定义的注册功能
	 */
	private final BeanDefinitionRegistry registry;


	public ComponentScanAnnotationParser(Environment environment, ResourceLoader resourceLoader,
			BeanNameGenerator beanNameGenerator, BeanDefinitionRegistry registry) {

		/**
		 *该对象存储所有的环境变量
		 */
		this.environment = environment;
		/**
		 * 资源加载器
		 */
		this.resourceLoader = resourceLoader;
		/**
		 * bean的名称生成器
		 */
		this.beanNameGenerator = beanNameGenerator;
		/**
		 * 该对象具有bean定义的注册功能
		 */
		this.registry = registry;
	}


	/**
	 *
	 * @param componentScan @ComponentScan的所有注解属性
	 * @param declaringClass
	 * @return
	 */
	public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {
	/**
	* 第一步:创建扫描包下bean的扫描器(scanner)
	 */
		ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,
				componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);

		/**
		 * 第二步:从@ComponentScan的注解属性里获取属性值,并赋值给scanner扫描器
		 * 1:添加Bean的名称生成器
		 */
		Class<? extends BeanNameGenerator> generatorClass = componentScan.getClass("nameGenerator");
		boolean useInheritedGenerator = (BeanNameGenerator.class == generatorClass);
		scanner.setBeanNameGenerator(useInheritedGenerator ? this.beanNameGenerator :
				BeanUtils.instantiateClass(generatorClass));

		/**
		 * 2:添加代理模式:基本不用,可忽略
		 */
		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));
		}

		/**
		 * 3:添加配置资源的正则表达式
		 */
		scanner.setResourcePattern(componentScan.getString("resourcePattern"));

		/**
		 * 4:添加包含过滤器
		 */
		for (AnnotationAttributes filter : componentScan.getAnnotationArray("includeFilters")) {
			for (TypeFilter typeFilter : typeFiltersFor(filter)) {
				scanner.addIncludeFilter(typeFilter);
			}
		}
		/**
		 * 5:添加排除过滤器
		 */
		for (AnnotationAttributes filter : componentScan.getAnnotationArray("excludeFilters")) {
			for (TypeFilter typeFilter : typeFiltersFor(filter)) {
				scanner.addExcludeFilter(typeFilter);
			}
		}

		/**
		 * 6:是否延迟加载
		 */
		boolean lazyInit = componentScan.getBoolean("lazyInit");
		if (lazyInit) {
			scanner.getBeanDefinitionDefaults().setLazyInit(true);
		}

		/**
		 * 7:添加指定扫描的包
		 */
		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);
		}

		/**
		 * 8:添加指定扫描的包
		 */
		for (Class<?> clazz : componentScan.getClassArray("basePackageClasses")) {
			basePackages.add(ClassUtils.getPackageName(clazz));
		}
		if (basePackages.isEmpty()) {
			basePackages.add(ClassUtils.getPackageName(declaringClass));
		}

		/**
		 * 9:添加一个默认的排除过滤器,主要用于排除程序启动的配置类。如果启动类也在扫描的包下,将会引起重复。因为扫描包的时也会将启动类扫描进来。
		 */
		scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) {
			@Override
			protected boolean matchClassName(String className) {
				return declaringClass.equals(className);
			}
		});
		/**
		 *第三步:调用scanner.doScan()接口,开始真正的扫描,描包下的bean,并返回BeanDefinition
		 */
		return scanner.doScan(StringUtils.toStringArray(basePackages));
	}
}

ClassPathBeanDefinitionScanner类的doScan()接口分析逻辑。

第一步:获取包下的所有BeanDefinition

第二步:下面的代码是对bean定义做一些额外的处理

第三步:注册BeanDefinition到IOC的指定容器里

public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateComponentProvider {
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) {
			/**
			 * 第一步:获取包下的所有BeanDefinition
			 */
			Set<BeanDefinition> candidates = findCandidateComponents(basePackage);

			/**
			 * 第二步:下面的代码是对bean定义做一些额外的处理
			 */
			for (BeanDefinition candidate : candidates) {
				/**
				 * Bean的作用域
				 */
				ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
				candidate.setScope(scopeMetadata.getScopeName());
				/**
				 * 生成Bean名称
				 */
				String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
				if (candidate instanceof AbstractBeanDefinition) {
					postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
				}

				/**
				 * 如果是注解类型的BeanDefinition,需要处理一些通用的注解属性。
				 * 主要包括下面这些注解处理:@Lazy、@Primary、@DependsOn、@Role、@Description
				 */
				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);
					/**
					 * 第三步:注册BeanDefinition到IOC的指定容器里
					 */
					registerBeanDefinition(definitionHolder, this.registry);
				}
			}
		}
		return beanDefinitions;
	}
}

该测试代码是为了测试ComponentScanAnnotationParser的parser()方法。注意需要修改ComponentScanAnnotationParser类的访问权限为public

@ComponentScan("interfaceTest.ComponentScanParser")
public class ComponentScanParserTest {

	public static void main(String[] args) {
		ComponentScanParserTest componentScanParserTest = new ComponentScanParserTest();
		componentScanParserTest.parse();
	}

	public void parse() {
		/**
		 * 将ComponentScanParserTest声明成一个BeanDefinition
		 */
		AnnotatedGenericBeanDefinition beanDefinition = new AnnotatedGenericBeanDefinition(ComponentScanParserTest.class);

		/**
		 * 获取@ComponentScan注解解析器
		 */
		ComponentScanAnnotationParser componentScanAnnotationParser = this.getComponentScanAnnotationParser();

		/**
		 * 获取类上的注解属性值
		 */
		AnnotationAttributes annotationAttributes = AnnotationAttributes.fromMap(beanDefinition.getMetadata().getAnnotationAttributes(ComponentScan.class.getName(), false));

		/**
		 * 根据注解属性值将包下的bean转换成BeanDifiniton
		 */
		Set<BeanDefinitionHolder> beanDefinitionHolders = componentScanAnnotationParser.parse(annotationAttributes, ComponentScanParserTest.class.getName());

		System.out.println(beanDefinitionHolders);


	}

	/**
	 * 获取ComponentScan注解解析器
	 *
	 * @return ComponentScanAnnotationParser:ComponentScan注解解析器
	 */
	public ComponentScanAnnotationParser getComponentScanAnnotationParser() {
		/**
		 * spring源码就是通过ComponentScan注解解析器解析@ComponentScan注解的。
		 */
		ComponentScanAnnotationParser componentScanAnnotationParser = new ComponentScanAnnotationParser(
				new StandardEnvironment(),
				new DefaultResourceLoader(),
				new AnnotationBeanNameGenerator(),
				new DefaultListableBeanFactory()
		);
		return componentScanAnnotationParser;
	}

}

最后

以上就是重要小白菜为你收集整理的第四章:@ComponentScan注解源码的分析的全部内容,希望文章能够帮你解决第四章:@ComponentScan注解源码的分析所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(47)

评论列表共有 0 条评论

立即
投稿
返回
顶部