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

前言

在分析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容器流程的时候再具体分析~~~

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏kubernetes中文社区

不管你是开发还是运维,微服务这些你得知道!

这几年在Java工程师招聘时,会看到很多人的简历都写着使用了Spring Cloud做微服务实现,使用Docker做自动化部署,并且也会把这些做为自己的亮点。而...

17510
来自专栏知了一笑

Java描述设计模式(07):适配器模式

缺省(接口)适配(Default Adapter)模式为一个接口提供缺省实现,这样子类型可以从这个缺省实现进行扩展,而不必从原有接口进行扩展。

6420
来自专栏全栈开发之路

SpringMVC学习

Spring MVC是一个基于MVC架构的用来简化web应用程序开发的应用开发框架,它是Spring的一部分,它和Struts2一样都属于表现层的框架。

7710
来自专栏后端开发你必须学会的干货

快速理解Java的三种代理模式

代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式,即通过代理对象访问目标对象。这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即...

6840
来自专栏后端开发你必须学会的干货

SpringIoC和SpringMVC的快速入门

IoC和AOP是Spring框架的两大特性,IoC和MVC的流程密不可分,可以看作是面向对象编程的实现;而AOP特性则是面向切面编程的体现,也是前者的补充,所以...

6420
来自专栏后端开发你必须学会的干货

快速了解Java线程池

还有很多其他的方法:比如:getQueue() 、getPoolSize() 、getActiveCount()、getCompletedTaskCount()...

12150
来自专栏JAVA葵花宝典

那些年,我们见过的 Java 服务端乱象

移动互联网的快速发展,出现了许多新机遇,很多创业者伺机而动;随着行业竞争加剧,互联网红利逐渐消失,很多创业公司九死一生。笔者在初创公司摸爬滚打数年,接触了各式各...

9510
来自专栏后端开发你必须学会的干货

Spring与后端模板引擎的故事

现在很多开发,都采用了前后端完全分离的模式,随着近几年前端工程化工具和MVC框架的完善,使得这种模式的维护成本逐渐降低。但是这种模式目前并不利于SEO(前后端分...

13830
来自专栏全栈开发之路

SSH架构/spring-security安全认证/LDAP账号打通

参考:https://blog.csdn.net/shan9liang/article/details/8803989

10230
来自专栏吟风者

Spring Boot 自定义事件

系统启动时,执行顺序为:application starting > application prepared > application ready

9710

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励