Spring5源码之Mybatis

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

这篇文章主要介绍了Spring5源码之Mybatis,通过源码底层来分析Spring与Mybatis的逻辑,需要的朋友可以参考一下。

通过上一篇Spring5整合Mybatis的使用案例,我们感受到了Spring为用户更加快捷地进行开发所做的努力,开发人员的工作效率大大提升。那我们就从sqlSessionFactory的创建开始分析源码。

1、sqlSessionFactory创建

我们从SqlSessionFactoryBean类的调用类的调用关系(如下图)可以找到我们感兴趣的两个接口FactoryBean和InitializingBean。 SqlSessionFactoryBean

InitializingBean:实现此接口的bean会在初始化时调用其afterPropertiesSet方法来进行bean的逻辑初始化。 FactoryBean:一旦某个bean实现此接口,那么通过getBean方法获取bean时其实是获取此类的getObject()返回实例。

我们首先以InitializingBean接口的afterPropertiesSet方法作为突破点。

  • 1、SqlSessionFactoryBean的初始化
  public void afterPropertiesSet() throws Exception {
    notNull(dataSource, "Property 'dataSource' is required");
    notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
    state((configuration == null && configLocation == null) || !(configuration != null && configLocation != null),
              "Property 'configuration' and 'configLocation' can not specified with together");

    this.sqlSessionFactory = buildSqlSessionFactory();
  }

protected SqlSessionFactory buildSqlSessionFactory() throws IOException {

   Configuration configuration;

   XMLConfigBuilder xmlConfigBuilder = null;
   if (this.configuration != null) {
     configuration = this.configuration;
     if (configuration.getVariables() == null) {
       configuration.setVariables(this.configurationProperties);
     } else if (this.configurationProperties != null) {
       configuration.getVariables().putAll(this.configurationProperties);
     }
   } else if (this.configLocation != null) {
     xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
     configuration = xmlConfigBuilder.getConfiguration();
   } else {
     if (LOGGER.isDebugEnabled()) {
       LOGGER.debug("Property 'configuration' or 'configLocation' not specified, using default MyBatis Configuration");
     }
     configuration = new Configuration();
     if (this.configurationProperties != null) {
       configuration.setVariables(this.configurationProperties);
     }
   }

   if (this.objectFactory != null) {
     configuration.setObjectFactory(this.objectFactory);
   }

   if (this.objectWrapperFactory != null) {
     configuration.setObjectWrapperFactory(this.objectWrapperFactory);
   }

   if (this.vfs != null) {
     configuration.setVfsImpl(this.vfs);
   }

   if (hasLength(this.typeAliasesPackage)) {
     String[] typeAliasPackageArray = tokenizeToStringArray(this.typeAliasesPackage,
         ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
     for (String packageToScan : typeAliasPackageArray) {
       configuration.getTypeAliasRegistry().registerAliases(packageToScan,
               typeAliasesSuperType == null ? Object.class : typeAliasesSuperType);
       if (LOGGER.isDebugEnabled()) {
         LOGGER.debug("Scanned package: '" + packageToScan + "' for aliases");
       }
     }
   }

   if (!isEmpty(this.typeAliases)) {
     for (Class<?> typeAlias : this.typeAliases) {
       configuration.getTypeAliasRegistry().registerAlias(typeAlias);
       if (LOGGER.isDebugEnabled()) {
         LOGGER.debug("Registered type alias: '" + typeAlias + "'");
       }
     }
   }

   if (!isEmpty(this.plugins)) {
     for (Interceptor plugin : this.plugins) {
       configuration.addInterceptor(plugin);
       if (LOGGER.isDebugEnabled()) {
         LOGGER.debug("Registered plugin: '" + plugin + "'");
       }
     }
   }

   if (hasLength(this.typeHandlersPackage)) {
     String[] typeHandlersPackageArray = tokenizeToStringArray(this.typeHandlersPackage,
         ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
     for (String packageToScan : typeHandlersPackageArray) {
       configuration.getTypeHandlerRegistry().register(packageToScan);
       if (LOGGER.isDebugEnabled()) {
         LOGGER.debug("Scanned package: '" + packageToScan + "' for type handlers");
       }
     }
   }

   if (!isEmpty(this.typeHandlers)) {
     for (TypeHandler<?> typeHandler : this.typeHandlers) {
       configuration.getTypeHandlerRegistry().register(typeHandler);
       if (LOGGER.isDebugEnabled()) {
         LOGGER.debug("Registered type handler: '" + typeHandler + "'");
       }
     }
   }

   if (this.databaseIdProvider != null) {//fix #64 set databaseId before parse mapper xmls
     try {
       configuration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));
     } catch (SQLException e) {
       throw new NestedIOException("Failed getting a databaseId", e);
     }
   }

   if (this.cache != null) {
     configuration.addCache(this.cache);
   }

   if (xmlConfigBuilder != null) {
     try {
       xmlConfigBuilder.parse();

       if (LOGGER.isDebugEnabled()) {
         LOGGER.debug("Parsed configuration file: '" + this.configLocation + "'");
       }
     } catch (Exception ex) {
       throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex);
     } finally {
       ErrorContext.instance().reset();
     }
   }

   if (this.transactionFactory == null) {
     this.transactionFactory = new SpringManagedTransactionFactory();
   }

   configuration.setEnvironment(new Environment(this.environment, this.transactionFactory, this.dataSource));

   if (!isEmpty(this.mapperLocations)) {
     for (Resource mapperLocation : this.mapperLocations) {
       if (mapperLocation == null) {
         continue;
       }

       try {
         XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
             configuration, mapperLocation.toString(), configuration.getSqlFragments());
         xmlMapperBuilder.parse();
       } catch (Exception e) {
         throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
       } finally {
         ErrorContext.instance().reset();
       }

       if (LOGGER.isDebugEnabled()) {
         LOGGER.debug("Parsed mapper file: '" + mapperLocation + "'");
       }
     }
   } else {
     if (LOGGER.isDebugEnabled()) {
       LOGGER.debug("Property 'mapperLocations' was not specified or no matching resources found");
     }
   }

   return this.sqlSessionFactoryBuilder.build(configuration);
 }

从方法中可以看到,尽管我们还是习惯于将Mybatis的配置与Spring的配置独立出来,但是,这并不代表Spring中的配置不支持直接配置。配置文件还可以支持其他多种属性,如:objectFactory、objectWrapperFactory、plugins、typeAliasesPackage、typeAliases、typeHandlersPackage、typeHandlers、databaseIdProvider、transactionFactory和mapperLocations等。

  • 2、获取SqlSessionFactoryBean的实例 由于SqlSessionFactoryBean实现了FactoryBean接口,所以当通过getBean方法获取对应的实例,其实是获取该类的getObject()方法返回的实例,也就是获取初始化后的sqlSessionFactory的属性。
 public SqlSessionFactory getObject() throws Exception {
   if (this.sqlSessionFactory == null) {
     afterPropertiesSet();
   }

   return this.sqlSessionFactory;
 }

2、MapperFactoryBean的创建

我们从SqlSessionFactoryBean类的调用类的调用关系(如下图)可以找到我们感兴趣的两个接口FactoryBean和InitializingBean。我们的分析还是从bean的初始化开始。 MapperFactoryBean

  • 1、MapperFactoryBean的初始化 因为实现了InitializingBean接口,Spring会保证在bean的初始化是首先调用afterPropertiesSet方法来完成其初始化逻辑,追踪父类发现afterPropertiesSet方法是在DaoSupport类实现,代码如下:
public final void afterPropertiesSet() throws IllegalArgumentException, BeanInitializationException {
	// 让抽象子类检查它们的配置。
	checkDaoConfig();

	// 让具体的实现初始化它们自己。
	try {
		initDao();
	}
	catch (Exception ex) {
		throw new BeanInitializationException("Initialization of DAO failed", ex);
	}
}

从方法代码逻辑来看,MapperFactoryBean的初始化包括对DAO配置的验证以及对DAO的初始化工作,其中initDao方法是模板方法,设计为留给子类做进一步逻辑处理。而checkDaoConfig才是我们分析的重点。

  protected void checkDaoConfig() {
    super.checkDaoConfig();

    notNull(this.mapperInterface, "Property 'mapperInterface' is required");

    Configuration configuration = getSqlSession().getConfiguration();
    if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
      try {
        configuration.addMapper(this.mapperInterface);
      } catch (Exception e) {
        logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", e);
        throw new IllegalArgumentException(e);
      } finally {
        ErrorContext.instance().reset();
      }
    }
  }

super.checkDaoConfig()在SqlSessionDaoSupport类中实现,代码如下:

 protected void checkDaoConfig() {
   notNull(this.sqlSession, "Property 'sqlSessionFactory' or 'sqlSessionTemplate' are required");
 }

结合代码,我们了解到对于DAO配置的验证,Spring做了以下几个方面的工作。 父类中对于sqlSession不为空的验证 sqlSession作为根据接口创建映射代理的接触类一定不可以为空,而sqlSession的初始化工作是在设定其sqlSessionFactory属性完成的。

 public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
   if (!this.externalSqlSession) {
     this.sqlSession = new SqlSessionTemplate(sqlSessionFactory);
   }
 }

映射接口的验证 接口是映射器的基础,sqlSession会根据接口动态创建相应的代理类,所以接口必不可少。 映射文件存在性验证 configuration.addMapper(this.mapperInterface)其实就是讲PersonMapper注册到映射类型中。如果你可以保证这个接口一定存在对应的映射文件,那么其实这个验证并没有必要。

  • 2、获取MapperFactoryBean的实例 由于MapperFactoryBean实现了FactoryBean的接口,所以当通过getBean方法获取对应实例的时候其实是获取改类的getObject()方法的实例。
 public T getObject() throws Exception {
   return getSqlSession().getMapper(this.mapperInterface);
 }

这段代码正是我们在提供Mybatis独立使用的时候的一个代码调用,Spring通过FactoryBean进行了封装。

3、MapperScannerConfigurer

我们在application.xml中配置了personMapper供需要时使用,但如果需要用到的映射器较多的话,采用这种配置方式疾苦显得很低效。为了解决这个问题,我们可以使用MapperScannerConfigurer,让它扫描特定的包,自动帮我们成批地创建映射器。这样一来,就能大大减少配置的工作量,比如我们将application.xml文件中的配置改成如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

	<!--<bean id="myTestBean" class="com.test.bean.MyTestBean"></bean>-->

	<!--配置数据源-->
	<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
		<property name="driverClassName" value="com.mysql.jdbc.Driver" />
		<property name="url" value="jdbc:mysql://127.0.0.1:3306/user?serverTimezone=Asia/Shanghai" />
		<property name="username" value="root" />
		<property name="password" value="123456" />
		<!--连接池启动时初始值-->
		<property name="initialSize" value="1" />
		<!--连接池的最大值-->
		<property name="maxActive" value="300" />
		<!--最大空闲值,当经过一个高峰时间后,连接池可以慢慢将已经用不到的连接慢慢释放一部分,一直减少到maxIdle为止-->
		<property name="maxIdle" value="2"/>
		<!--最小空闲值,当空闲的连接数少于阀值时,连接池就会预申请一些连接,以免洪峰来时来不及申请-->
		<property name="minIdle" value="1"/>
	</bean>

	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="configLocation" value="classpath:Mybatis-Configuration.xml"></property>
		<property name="dataSource" ref="dataSource"></property>
	</bean>


<!-- 注释原有代码	
<bean id="personMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">-->
<!--		<property name="mapperInterface" value="com.test.mybatis.mapper.PersonMapper"></property>-->
<!--		<property name="sqlSessionFactory" ref="sqlSessionFactory"></property>-->
<!--	</bean>
-->

	<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
		<property name="basePackage" value="com.test.mybatis.mapper"></property>
 	</bean>

</beans>

通过Spring就会帮助我们对com.test.mybatis.mapper下面的所有接口进行自动的注入,而不需要为每个接口重复在Spring配置文件中进行声明。那么这个功能又是如何做到的呢?首先看一下MapperScannerConfigurer类的调用关系,如下所示: MapperScannerConfigurer 我们又看到了令人感兴趣的接口InitializingBean,马上查找类的afterPropertiesSet方法来查看类初始化逻辑。

 public void afterPropertiesSet() throws Exception {
   notNull(this.basePackage, "Property 'basePackage' is required");
 }

很遗憾,分析并没有想我们之前那样顺利,afterPropertiesSet除了一句对basePackage属性的验证外没有太多的逻辑实现。我们再次看MapperScannerConfigurer类的调用关系图发现BeanDefinitionRegistryPostProcessor与BeanFactoryPostProcessor,Spring在初始化的过程中同样会保证这两个接口的调用。

首先查看MapperScannerConfigurer类对BeanFactoryPostProcessor的接口的实现。

 public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
   // left intentionally blank
 }

没有任何逻辑,只能说明我们找错地方了,继续找,查看MapperScannerConfigurer类对BeanDefinitionRegistryPostProcessor的接口的实现。

 public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
   if (this.processPropertyPlaceHolders) {
     processPropertyPlaceHolders();
   }

   ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
   scanner.setAddToConfig(this.addToConfig);
   scanner.setAnnotationClass(this.annotationClass);
   scanner.setMarkerInterface(this.markerInterface);
   scanner.setSqlSessionFactory(this.sqlSessionFactory);
   scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
   scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
   scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
   scanner.setResourceLoader(this.applicationContext);
   scanner.setBeanNameGenerator(this.nameGenerator);
   scanner.registerFilters();
   scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
 }

Bingo!这次找对地方了。大致看一下代码实现,正是完成了对指定路径扫描的逻辑,那么,我们就以此为入口,详细分析MapperScannerConfigurer所提供的逻辑实现。

  • 1、processPropertyPlaceHolders属性处理
private void processPropertyPlaceHolders() {
   Map<String, PropertyResourceConfigurer> prcs = applicationContext.getBeansOfType(PropertyResourceConfigurer.class);

   if (!prcs.isEmpty() && applicationContext instanceof ConfigurableApplicationContext) {
     BeanDefinition mapperScannerBean = ((ConfigurableApplicationContext) applicationContext)
         .getBeanFactory().getBeanDefinition(beanName);
     // PropertyResourceConfigurer不公开要显式执行的任何方法
     DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
     factory.registerBeanDefinition(beanName, mapperScannerBean);

     for (PropertyResourceConfigurer prc : prcs.values()) {
       prc.postProcessBeanFactory(factory);
     }

     PropertyValues values = mapperScannerBean.getPropertyValues();

     this.basePackage = updatePropertyValue("basePackage", values);
     this.sqlSessionFactoryBeanName = updatePropertyValue("sqlSessionFactoryBeanName", values);
     this.sqlSessionTemplateBeanName = updatePropertyValue("sqlSessionTemplateBeanName", values);
   }
 }
通过以上方法的逻辑,我们可以看到BeanDefinitionRegistry会在应用启动的时候调用,并且会早于BeanFactoryPostProcessor的调用,这意味着PropertyResourceConfigurer还没有被加载所有对于属性文件的引用将会失效。为避免此种情况发生,此方法手动找出定义的PropertyResourceConfigurers并进行提前调用以保证对于属性的引用可以正常工作。
  • 2、根据配置属性生成过滤器 在postProcessBeanDefinitionRegistry方法可以看到,配置中支持很多属性的设定,但是我们感兴趣的或者影响扫描结果的并不多,属性设置后通过在scanner.registerFilters()代码中生成对应的过滤器来控制扫描结果。
public void registerFilters() {
   boolean acceptAllInterfaces = true;

   // 对于annotationClass属性的处理
   if (this.annotationClass != null) {
     addIncludeFilter(new AnnotationTypeFilter(this.annotationClass));
     acceptAllInterfaces = false;
   }

   // 对于markerInterface属性的处理
   if (this.markerInterface != null) {
     addIncludeFilter(new AssignableTypeFilter(this.markerInterface) {
       @Override
       protected boolean matchClassName(String className) {
         return false;
       }
     });
     acceptAllInterfaces = false;
   }

   if (acceptAllInterfaces) {
     // 默认包含接受所有类的过滤器
     addIncludeFilter(new TypeFilter() {
       @Override
       public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
         return true;
       }
     });
   }

   // 排除package-info.java
   addExcludeFilter(new TypeFilter() {
     @Override
     public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
       String className = metadataReader.getClassMetadata().getClassName();
       return className.endsWith("package-info");
     }
   });
 }

代码中得知,根据之前的属性配置生成了对应的过滤器 annotationClass属性的处理 如果annotationClass不为空,表示用户设置了此属性,那么就要根据此属性生成过滤器以保证达到用户想要的效果,而封装此属性的过滤器就是AnnotationTypeFilter。AnnotationTypeFilter保证在扫描对应Java文件时只接受标记有注解为annotationClass的接口。 markerInterface属性的处理 如果markerInterface不为空,表示用户设置了此属性,那么就要根据此属性生成过滤器以保证达到用户想要的效果,而封装此属性的过滤器就是实现AssignableTypeFilter接口的局部类。表示扫描过程中只有实现markerInterface接口才会被接受。 全局默认处理 在上面两个属性中如果存在其中任何属性,acceptAllInterfaces值将会改变,但是如果用户没有设定以上两个属性,那么Spring会为我们增加一个默认的过滤器实现TypeFilter接口的局部类,旨在接受所有接口文件。 package-info.java处理 对于命名为package-info的Java文件,默认不作为逻辑实现接口, 将其排除掉,使用TypeFilter接口的局部类实现match方法。 从上面的函数,我们看出,控制扫描文件Spring通过不同的过滤器完成,这些定义的过滤器记录在includeFilters和excludeFilters属性中。

public void addIncludeFilter(TypeFilter includeFilter) {
	this.includeFilters.add(includeFilter);
}

public void addExcludeFilter(TypeFilter excludeFilter) {
	this.excludeFilters.add(0, excludeFilter);
}
  • 3、扫描Java文件 设置了相关属性已经生成了对应的过滤器后便可以进行文件的扫描了,扫描工作是由ClassPathBeanDefinitionScanner类型的实例scanner中的scan方法完成的。
public int scan(String... basePackages) {
	int beanCountAtScanStart = this.registry.getBeanDefinitionCount();

	doScan(basePackages);

	// 如果配置了includeAnnotationConfig,则注册对应注解的处理器以保证注解功能的正常使用。
	if (this.includeAnnotationConfig) {
		AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
	}

	return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
}

scan是全局方法,扫描工作通过 doScan(basePackages)委托给了doScan方法,同时还包括了includeAnnotationConfig属性处理,AnnotationConfigUtils.registerAnnotationConfigProcessors()主要是完成了对于注解处理器的简单注册。

ClassPathMapperScanner

public Set<BeanDefinitionHolder> doScan(String... basePackages) {
   Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);

   if (beanDefinitions.isEmpty()) {
   	 // 如果没有扫描到任何文件就发出警告
     logger.warn("No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
   } else {
     processBeanDefinitions(beanDefinitions);
   }

   return beanDefinitions;
 }


private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
  GenericBeanDefinition definition;
   for (BeanDefinitionHolder holder : beanDefinitions) {
     definition = (GenericBeanDefinition) holder.getBeanDefinition();

     if (logger.isDebugEnabled()) {
       logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName() 
         + "' and '" + definition.getBeanClassName() + "' mapperInterface");
     }

     // mapper接口是bean的原始类
     // 但是,bean的实际类是MapperFactoryBean
     definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName()); // issue #59
     definition.setBeanClass(this.mapperFactoryBean.getClass());

     definition.getPropertyValues().add("addToConfig", this.addToConfig);

     boolean explicitFactoryUsed = false;
     if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
       definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
       explicitFactoryUsed = true;
     } else if (this.sqlSessionFactory != null) {
       definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
       explicitFactoryUsed = true;
     }

     if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
       if (explicitFactoryUsed) {
         logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
       }
       definition.getPropertyValues().add("sqlSessionTemplate", new RuntimeBeanReference(this.sqlSessionTemplateBeanName));
       explicitFactoryUsed = true;
     } else if (this.sqlSessionTemplate != null) {
       if (explicitFactoryUsed) {
         logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
       }
       definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
       explicitFactoryUsed = true;
     }

     if (!explicitFactoryUsed) {
       if (logger.isDebugEnabled()) {
         logger.debug("Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
       }
       definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
     }
   }
 }

此时虽然还没有完成介绍到扫描的过程,但是我们也应该立即了Spring中对于自动扫描的注册,声明MapperFactoryBean类型的对应的bean目的是不需要我们对于每个接口都注册来了一个MapperFactoryBean类型的对应的bean。

ClassPathBeanDefinitionScanner

/**
 * 在指定的基本包中执行扫描,返回注册的bean定义。
 */
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) {
		// 扫描basePackage路径下java 文件
		Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
		for (BeanDefinition candidate : candidates) {
		    //解析scope属性
			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) {
			// 如果是AnnotatedBeanDefinition类型的bean,需要检测常用注解如:Lazy、Primary、DependsOn和Role等
				AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
			}
			// 校验当前bean是否已经注册
			if (checkCandidate(beanName, candidate)) {
				BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
				// 如果当前bean是用于生成代理的bean,那么需要进一步处理
				definitionHolder =
						AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
				beanDefinitions.add(definitionHolder);
				registerBeanDefinition(definitionHolder, this.registry);
			}
		}
	}
	return beanDefinitions;
}

/**
* 扫描类路径以查找候选组件
*/
public Set<BeanDefinition> findCandidateComponents(String basePackage) {
	if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
		return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
	}
	else {
		return scanCandidateComponents(basePackage);
	}
}


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;
}

findCandidateComponents方法根据传入的包路径信息并结合类文件路径拼接成文件的绝代路径,同时完成了文件的扫描过程并且根据对应的文件生成了对应的bean,使用ScannedGenericBeanDefinition类型的bean承载信息,bean中只记录了resource和source信息。在这里,我们看到isCandidateComponent(metadataReader)方法用于判断当前扫描的文件是否符合要求。


/**
 * 确定给定的类是否不匹配任何排除筛选器并匹配至少一个包含过滤器。
 */
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;
}

我们看到了之前加入的过滤器的两个属性excludeFilters和includeFilters并且知道对应的文件是否符合要求是根据过滤器中的match方法所返回的信息来判断,当然用户可以实现并注册满足自己业务逻辑的过滤器来控制扫描的结果。MetadataReader类中有你过滤所需的全部文件信息。至此我们完成了文件的扫描分析过程。

如果您觉得有帮助,欢迎收藏点赞哦 ~ ~ 多谢~