首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【小家Spring】Spring容器加载Bean定义信息的两员大将:AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner

【小家Spring】Spring容器加载Bean定义信息的两员大将:AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner

作者头像
YourBatman
发布2019-09-03 16:33:21
2.1K0
发布2019-09-03 16:33:21
举报
文章被收录于专栏:BAT的乌托邦BAT的乌托邦
前言

在分析Spring IOC容器启动流程的时候,在加载Bean定义信息BeanDefinition的时候,用到了两个非常关键的类:AnnotatedBeanDefinitionReaderClassPathBeanDefinitionScanner。它俩完成对Bean信息的加载。

因此为了更加顺畅的去理解Bean的加载的一个过程,本文主要介绍Spring的这两员大将的一个初始化过程,以及它俩扮演的重要角色

环境准备

因为我们只需要了解Bean的加载,所以只需要启动一个容器就行,并不需要web环境,因此本文用一个相对简单的环境,来进行讲解,如下:

	@ComponentScan(value = "com.fsx", excludeFilters = {
	        @Filter(type = FilterType.ANNOTATION, classes = {Controller.class}),
	        //排除掉web容器的配置文件,否则会重复扫描
	        @Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {AppConfig.class}),
	})
	@Configuration
	public class RootConfig {
	
	    @Bean
	    public Parent parent() {
	        return new Parent();
	    }
	
	}


    public static void main(String[] args) {
        // 备注:此处只能用RootConfig,而不能AppConfig(启动报错),因为它要web容器支持,比如Tomcat
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(RootConfig.class);
        System.out.println(applicationContext.getBean(Parent.class)); //com.fsx.bean.Parent@639c2c1d
    }

环境准备好了,启动之后也能正常打印出Bean的信息。因此接下来,就是要去分析源码,看看这两大工具起的作用

IOC容器加载Bean定义信息分析

AnnotationConfigApplicationContext(spring-context包下)的继承图谱如下:

需要注意的是,我们在Tomcat等web环境下的容器类为:AnnotationConfigWebApplicationContext,它在spring-web包下

在这里插入图片描述
在这里插入图片描述

Spring容器里通过BeanDefinition对象来表示Bean,BeanDefinition描述了Bean的配置信息。 而BeanDefinitionRegistry接口提供了向容器注册,删除,获取BeanDefinition对象的方法。

简单来说,BeanDefinitionRegistry可以用来管理BeanDefinition,所以理解AnnotationConfigApplicationContext很关键,它是spring加载bean,管理bean的最重要的类。

源码跟踪:

	public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
		this();
		// 把该配置类(们)注册进来
		register(annotatedClasses);
		// 容器启动核心方法
		refresh();
	}

this()如下:

	//AnnotatedBeanDefinitionReader是一个读取注解的Bean读取器,这里将this传了进去。
	public AnnotationConfigApplicationContext() {
		this.reader = new AnnotatedBeanDefinitionReader(this);
		this.scanner = new ClassPathBeanDefinitionScanner(this);
	}
	
	//从上面这个构造函数可以顺便提一句:如果你仅仅是这样ApplicationContext applicationContext = new AnnotationConfigApplicationContext()
	// 容器是不会启动的(也就是不会执行refresh()的),这时候需要自己之后再手动启动容器

进而再看看register()方法:

	public void register(Class<?>... annotatedClasses) {
		Assert.notEmpty(annotatedClasses, "At least one annotated class must be specified");
		this.reader.register(annotatedClasses);
	}

	public void register(Class<?>... annotatedClasses) {
		for (Class<?> annotatedClass : annotatedClasses) {
			registerBean(annotatedClass);
		}
	}

	public void registerBean(Class<?> annotatedClass) {
		doRegisterBean(annotatedClass, null, null, null);
	}

实际逻辑在doRegisterBean()此方法上:

	<T> void doRegisterBean(Class<T> annotatedClass, @Nullable Supplier<T> instanceSupplier, @Nullable String name,
			@Nullable Class<? extends Annotation>[] qualifiers, BeanDefinitionCustomizer... definitionCustomizers) {
		
		// 先把此实体类型转换为一个BeanDefinition
		AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);
		//abd.getMetadata() 元数据包括注解信息、是否内部类、类Class基本信息等等
		// 此处由conditionEvaluator#shouldSkip去过滤,此Class是否是配置类。
		// 大体逻辑为:必须有@Configuration修饰。然后解析一些Condition注解,看是否排除~
		if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
			return;
		}

		abd.setInstanceSupplier(instanceSupplier);
		// 解析Scope
		ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
		abd.setScope(scopeMetadata.getScopeName());
		// 得到Bean的名称 一般为首字母小写(此处为AnnotationBeanNameGenerator)
		String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));

		// 设定一些注解默认值,如lazy、Primary等等
		AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
		// 解析qualifiers,若有此注解  则primary都成为true了
		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);
		}

		// 下面位解析Scope是否需要代理,最后把这个Bean注册进去
		BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
		definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
		BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
	}
AnnotatedBeanDefinitionReader初始化,构造器如下
	public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) {
		this(registry, getOrCreateEnvironment(registry));
	}

	public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
		Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
		Assert.notNull(environment, "Environment must not be null");
		this.registry = registry;
			
		//ConditionEvaluator完成条件注解的判断,在后面的Spring Boot中有大量的应用
		this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
		//这句会把一些自动注解处理器加入到AnnotationConfigApplicationContext下的BeanFactory的BeanDefinitions中  具体见下面
		AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
	}

这里将AnnotationConfigApplicationContext注册为管理BeanDefinition的BeanDefinitionRegistry,也就是说,spring中bean的管理完全交给了AnnotationConfigApplicationContext

AnnotationConfigUtils#registerAnnotationConfigProcessors() 我们要用一些注解比如:@Autowired/@Required/@Resource都依赖于各种各样的BeanPostProcessor来解析(AutowiredAnnotation、RequiredAnnotation、CommonAnnotationBeanPostProcessor等等)

但是向这种非常常用的,让调用者自己去申明,显然使用起来就过重了。所以Spring为我们提供了一种极为方便注册这些BeanPostProcessor的方式(若是xml方式,配置<context:annotation- config/>,若是全注解驱动的ApplicationContext,就默认会执行)

	public static void registerAnnotationConfigProcessors(BeanDefinitionRegistry registry) {
		registerAnnotationConfigProcessors(registry, null);
	}

	public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
			BeanDefinitionRegistry registry, @Nullable Object source) {
		
		// 把我们的beanFactory从registry里解析出来
		DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry);
		if (beanFactory != null) {
			if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) {
				beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
			}
			// 相当于如果没有这个AutowireCandidateResolver,就给设置一份ContextAnnotationAutowireCandidateResolver
			if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) {
				beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
			}
		}
		
		//这里初始长度放4  是因为大多数情况下,我们只会注册4个BeanPostProcessor 如下(不多说了)
		// BeanDefinitionHolder解释:持有name和aliases,为注册做准备
		// Spring 4.2之后这个改成6我觉得更准确点
		Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(4);

		if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
			RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
			def.setSource(source);
			beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
		}

		if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
			RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
			def.setSource(source);
			beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
		}

		if (!registry.containsBeanDefinition(REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
			RootBeanDefinition def = new RootBeanDefinition(RequiredAnnotationBeanPostProcessor.class);
			def.setSource(source);
			beanDefs.add(registerPostProcessor(registry, def, REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
		}

		// Check for JSR-250 support, and if present add the CommonAnnotationBeanPostProcessor.
		// 支持JSR-250的一些注解:@Resource、@PostConstruct、@PreDestroy等
		if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) {
			RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);
			def.setSource(source);
			beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
		}

		// Check for JPA support, and if present add the PersistenceAnnotationBeanPostProcessor.
		// 若导入了对JPA的支持,那就注册JPA相关注解的处理器
		if (jpaPresent && !registry.containsBeanDefinition(PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)) {
			RootBeanDefinition def = new RootBeanDefinition();
			try {
				def.setBeanClass(ClassUtils.forName(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME,
						AnnotationConfigUtils.class.getClassLoader()));
			}
			catch (ClassNotFoundException ex) {
				throw new IllegalStateException(
						"Cannot load optional framework class: " + PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, ex);
			}
			def.setSource(source);
			beanDefs.add(registerPostProcessor(registry, def, PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME));
		}
	

		// 下面两个类,是Spring4.2之后加入进来的,为了更好的使用Spring的事件而提供支持 
		// 支持了@EventListener注解,我们可以通过此注解更方便的监听事件了(Spring4.2之后)
		// 具体这个Processor和ListenerFactory怎么起作用的,且听事件专题分解
		if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) {
			RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class);
			def.setSource(source);
			beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME));
		}
		if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) {
			RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class);
			def.setSource(source);
			beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME));
		}

		return beanDefs;
	}

我们发现:内部定义的class都是带internal的

  • ConfigurationClassPostProcessor是一个BeanFactoryPostProcessorBeanDefinitionRegistryPostProcessor处理器,BeanDefinitionRegistryPostProcessor的处理方法能处理@Configuration等注解。ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry()方法内部处理@Configuration@Import@ImportResource和类内部的@Bean

ConfigurationClassPostProcessor类继承了BeanDefinitionRegistryPostProcessor。BeanDefinitionRegistryPostProcessor类继承了BeanFactoryPostProcessor。 通过BeanDefinitionRegistryPostProcessor可以创建一个特别后置处理器来将BeanDefinition添加到BeanDefinitionRegistry中。它和BeanPostProcessor不同BeanPostProcessor只是在Bean初始化的时候有个钩子让我们加入一些自定义操作;而BeanDefinitionRegistryPostProcessor可以让我们在BeanDefinition中添加一些自定义操作。在Mybatis与Spring的整合中,就利用到了BeanDefinitionRegistryPostProcessor来对Mapper的BeanDefinition进行了后置的自定义处理

  • AutowiredAnnotationBeanPostProcessor是用来处理@Autowired注解和@Value注解的
  • RequiredAnnotationBeanPostProcessor这是用来处理@Required注解
  • CommonAnnotationBeanPostProcessor提供对JSR-250规范注解的支持@javax.annotation.Resource、@javax.annotation.PostConstruct和@javax.annotation.PreDestroy等的支持。
  • EventListenerMethodProcessor提供@PersistenceContext的支持。
  • EventListenerMethodProcessor提供@ EventListener 的支持。@ EventListener实在spring4.2之后出现的,可以在一个Bean的方法上使用@EventListener注解来自动注册一个ApplicationListener

到此AnnotatedBeanDefinitionReader初始化完毕。 总结一下,AnnotatedBeanDefinitionReade读取器用来加载class类型的配置,在它初始化的时候,会预先注册一些BeanPostProcessorBeanFactoryPostProcessor,这些处理器会在接下来的spring初始化流程中被调用

ClassPathBeanDefinitionScanner的初始化:

跟踪构造函数的重载,最终都到这里:

	public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,
			Environment environment, @Nullable ResourceLoader resourceLoader) {

		Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
		this.registry = registry;
		
		//useDefaultFilters为true,所以此处一般都会执行
		// 当然我们也可以设置为false,比如@ComponentScan里就可以设置为false,只扫描指定的注解/类等等
		if (useDefaultFilters) {
			registerDefaultFilters();
		}
		// 设置环境
		setEnvironment(environment);
		// 详情如下:  这里resourceLoader传值,还是我们的工厂。否则为null
		setResourceLoader(resourceLoader);
	}

registerDefaultFilters()

	protected void registerDefaultFilters() {
		// 这里需要注意,默认情况下都是添加了@Component这个注解的
		//(相当于@Service @Controller @Respository等都会扫描,因为这些注解都属于@Component)  另外@Configuration也属于哦
		this.includeFilters.add(new AnnotationTypeFilter(Component.class));
		ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
		
		//下面两个 是兼容JSR-250的@ManagedBean和330的@Named注解
		try {
			this.includeFilters.add(new AnnotationTypeFilter(
					((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));
			logger.debug("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.debug("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");
		} catch (ClassNotFoundException ex) {
			// JSR-330 API not available - simply skip.
		}
		
		// 所以,如果你想Spring连你自定义的注解都扫描,自己实现一个AnnotationTypeFilter就可以啦
	}

ClassPathBeanDefinitionScanner继承于ClassPathScanningCandidateComponentProvider,它内部维护有两个final类型的List:这两个对象在执行本类的scanCandidateComponents()方法时就会起作用。

	//includeFilters中的就是满足过滤规则的
	private final List<TypeFilter> includeFilters = new LinkedList<>();
	//excludeFilters则是不满足过滤规则的
	private final List<TypeFilter> excludeFilters = new LinkedList<>();

ClassPathScanningCandidateComponentProvider#setResourceLoaderResourcePatternResolver,MetadataReaderFactory和CandidateComponentsIndex设定初始值。

	@Override
	public void setResourceLoader(@Nullable ResourceLoader resourceLoader) {
		this.resourcePatternResolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);
		this.metadataReaderFactory = new CachingMetadataReaderFactory(resourceLoader);
		// Spring5以后才有这句,优化了bean扫描
		this.componentsIndex = CandidateComponentsIndexLoader.loadIndex(this.resourcePatternResolver.getClassLoader());
	}

备注:若要使用Spring5 的这个功能,需要添加如下包。这样子当工程重新编译的时候(编译期),会在自动生成META-INF/spring-components。然后我们在启动用@ComponentScan扫描时,直接读取这个文件即可,极大的提高了Spring启动的速度。而这期间,可以使用Spring5.0最新提供的注解@Indexed来配合使用。 需要注意的是:这种方式也是存在缺陷的,具体缺陷请参考官方文档:https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#beans-scanning-index

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context-indexer</artifactId>
        <version>5.0.7.RELEASE</version>
        <optional>true</optional>
    </dependency>

然后在运行后会生成一个 META-INF/spring.components 的文件,之后只要运行工程发现这个文件都会直接使用他。可以通过环境变量或工程根目录的spring.properties中设置spring.index.ignore=ture来禁用这个功能 (这个功能如果没有什么明确的需求,慎重使用,会提高工程的管理成本。)

  • ResourcePatternResolver是一个接口,继承了ResourceLoader,可以用来获取Resource 实例。返回的实例为PathMatchingResourcePatternResolver类型
  • MetadataReaderFactory用于解析资源信息对应的元数据,这里返回的实例为:CachingMetadataReaderFactory,带有缓存的
  • CandidateComponentsIndexLoader.loadIndex () 方法是spring5.0以后加入的新特性,Spring Framework 5 改进了扫描和识别组件的方法,使大型项目的性能得到提升。(具体是通过编译器完成扫描,并且往本地写索引,然后启动的时候再去扫描索引即可的思路)

至此,ClassPathBeanDefinitionScanner初始化完毕,总结一下: ClassPathBeanDefinitionScanner是一个扫描指定类路径中注解Bean定义的扫描器,在它初始化的时候,会初始化一些需要被扫描的注解,初始化用于加载包下的资源的Loader

AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner的初始化是spring上线文初始化的起点,很多预加载的类会在spring接下来的初始化中发挥重要作用

包扫描的启动方式
    public static void main(String[] args) {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext("com.fsx.config");
        System.out.println(applicationContext.getBean(Parent.class)); //com.fsx.bean.Parent@639c2c1d
    }

启动函数为:

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

	public void scan(String... basePackages) {
		Assert.notEmpty(basePackages, "At least one base package must be specified");
		// 核心在这个scan方法里,见下面
		this.scanner.scan(basePackages);
	}
在这里插入图片描述
在这里插入图片描述

下面就是重点看看doScan()方法

	protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
		Assert.notEmpty(basePackages, "At least one base package must be specified");
		// 装载扫描到的Bean
		Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
		for (String basePackage : basePackages) {
	
			// 这个方法是最重点,把扫描到的Bean就放进来了(比如此处只有RootConfig一个Bean定义,是个配置类)
			// 这个是重点,会把该包下面所有的Bean都扫描进去。Spring5和一下的处理方式不一样哦~
			Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
			for (BeanDefinition candidate : candidates) {
				// 拿到Scope元数据:此处为singleton
				ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
				candidate.setScope(scopeMetadata.getScopeName());
				// 生成Bean的名称,默认为首字母小写。此处为"rootConfig"
				String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);

				// 此处为扫描的Bean,为ScannedGenericBeanDefinition,所以肯定为true
				// 因此进来,执行postProcessBeanDefinition(对Bean定义信息做)   如下详解
				// 注意:只是添加些默认的Bean定义信息,并不是执行后置处理器~~~
				if (candidate instanceof AbstractBeanDefinition) {
					postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
				}
				// 显然,此处也是true  也是完善比如Bean上的一些注解信息:比如@Lazy、@Primary、@DependsOn、@Role、@Description   @Role注解用于Bean的分类分组,没有太大的作用
				if (candidate instanceof AnnotatedBeanDefinition) {
					AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
				}
				// 检查这个Bean  比如
				//如果dao包(一般配置的basePakage是这个)下的类是符合mybaits要求的则向spring IOC容器中注册它的BeanDefinition  所以这步检查第三方Bean的时候有必要检查一下
				if (checkCandidate(beanName, candidate)) {
					BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
					
					//AnnotationConfigUtils类的applyScopedProxyMode方法根据注解Bean定义类中配置的作用域@Scope注解的值,为Bean定义应用相应的代理模式,主要是在Spring面向切面编程(AOP)中使用
					definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
					beanDefinitions.add(definitionHolder);

					// 注意 注意 注意:这里已经吧Bean注册进去工厂了,所有doScan()方法不接收返回值,也是没有任何问题的。。。。
					registerBeanDefinition(definitionHolder, this.registry);
				}
			}
		}
		return beanDefinitions;
	}

ClassPathScanningCandidateComponentProvider#findCandidateComponents

	public Set<BeanDefinition> findCandidateComponents(String basePackage) {
		// 上面说过了CandidateComponentsIndex是Spring5提供的优化扫描的功能
		// 显然这里编译器我们没有写META-INF/spring.components索引文件,所以此处不会执行Spring5 的扫描方式,所以我暂时不看了(超大型项目才会使用Spring5的方式)
		if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
			return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
		} else {
			// Spring 5之前的方式(绝大多数情况下,都是此方式)
			return scanCandidateComponents(basePackage);
		}
	}

scanCandidateComponents:根据basePackage扫描候选的组件们(非常重要)

	private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
		Set<BeanDefinition> candidates = new LinkedHashSet<>();
		try {
			// 1.根据指定包名 生成包搜索路径
			//通过观察resolveBasePackage()方法的实现, 我们可以在设置basePackage时, 使用形如${}的占位符, Spring会在这里进行替换 只要在Enviroment里面就行
			// 本次值为:classpath*:com/fsx/config/**/*.class
			String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
					resolveBasePackage(basePackage) + '/' + this.resourcePattern;
			
			//2. 资源加载器 加载搜索路径下的 所有class 转换为 Resource[]
			// 拿着上面的路径,就可以getResources获取出所有的.class类,这个很强大~~~
			// 真正干事的为:PathMatchingResourcePatternResolver#getResources方法
			// 此处能扫描到两个类AppConfig(普通类,没任何注解标注)和RootConfig。所以接下里就是要解析类上的注解,以及过滤掉不是候选的类(比如AppConfig)
			
			// 注意:这里会拿到类路径下(不包含jar包内的)的所有的.class文件 可能有上百个,然后后面再交给后面进行筛选~~~~~~~~~~~~~~~~(这个方法,其实我们也可以使用)
			// 当然和getResourcePatternResolver和这个模版有关
			Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);

			// 记录日志(下面我把打印日志地方都删除)
			boolean traceEnabled = logger.isTraceEnabled();
			boolean debugEnabled = logger.isDebugEnabled();

			// 接下来的这个for循环:就是把一个个的resource组装成
			for (Resource resource : resources) {
				//文件必须可读 否则直接返回空了
				if (resource.isReadable()) {
					try {

						//读取类的 注解信息 和 类信息 ,两大信息储存到  MetadataReader
						MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
				
						// 根据TypeFilter过滤排除组件。因为AppConfig没有标准@Component或者子注解,所以肯定不属于候选组件  返回false
						// 注意:这里一般(默认处理的情况下)标注了默认注解的才会true,什么叫默认注解呢?就是@Component或者派生注解。还有javax....的,这里省略啦
						if (isCandidateComponent(metadataReader)) {
						
							//把符合条件的 类转换成 BeanDefinition
							ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
							sbd.setResource(resource);
							sbd.setSource(resource);
							
							// 再次判断 如果是实体类 返回true,如果是抽象类,但是抽象方法 被 @Lookup 注解注释返回true (注意 这个和上面那个是重载的方法) 
							// 这和上面是个重载方法  个人觉得旨在处理循环引用以及@Lookup上
							if (isCandidateComponent(sbd)) {
								candidates.add(sbd);
							}
						}
					} 
				}
			}
		}
		return candidates;
	}

// 备注:此时ComponentScan这个注解还并没有解析

ClassPathBeanDefinitionScanner#postProcessBeanDefinition

	protected void postProcessBeanDefinition(AbstractBeanDefinition beanDefinition, String beanName) {
		// 位Bean定义 执行些默认的信息
		// BeanDefinitionDefaults是个标准的javaBean,有一些默认值
		beanDefinition.applyDefaults(this.beanDefinitionDefaults);

		// 自动依赖注入 匹配路径(此处为null,不进来)
		if (this.autowireCandidatePatterns != null) {
			beanDefinition.setAutowireCandidate(PatternMatchUtils.simpleMatch(this.autowireCandidatePatterns, beanName));
		}
	}

AbstractBeanDefinition#applyDefaults:

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

就这样,扫描的Bean就全部注册完成了(若你的Config比较多的情况下,用扫描的方式更加简单些~)

总结

观看Spring源码这么久,最大的感受就是:组件繁多,但管理有序。它完美的实现了单一职责的设计原则,谁的事情谁去干,这点体现得非常好。只有遵循了这个原则,才能做到无状态和组件化,达到随意装配的效果~ 另外,还有一种设计思想就是预加载。什么组件在什么时候加载,什么时候可以预加载进去,都是设计的精妙之处

理解ClassPathBeanDefinitionScanner的工作原理,可以帮助理解Spring IOC 容器的初始化过程。 同时对理解MyBatis 的 Mapper 扫描 也是有很大的帮助。 因为 MyBatisMapperScannerConfigurer底层实现也是一个ClassPathBeanDefinitionScanner的子类。就像我们自定义扫描器那样,自定定义了 过滤器的过滤规则

这两名大将还提供了比如扫描包的核心方法,这个在讲解具体IOC容器流程的时候再具体分析~~~

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019年03月01日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 环境准备
  • IOC容器加载Bean定义信息分析
    • AnnotatedBeanDefinitionReader初始化,构造器如下
      • ClassPathBeanDefinitionScanner的初始化:
      • 包扫描的启动方式
      • 总结
      相关产品与服务
      容器服务
      腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档