
上一章,我们介绍了在AnnotationConfigApplicationContext初始化的时候,会创建AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner两个对象:
public class AnnotationConfigApplicationContext extends GenericApplicationContext implements AnnotationConfigRegistry {
……
public AnnotationConfigApplicationContext() {
this.reader = new AnnotatedBeanDefinitionReader(this);
this.scanner = new ClassPathBeanDefinitionScanner(this);
}
……
}我们已经知道AnnotatedBeanDefinitionReader对象创建的大致流程,AnnotatedBeanDefinitionReader是用来注册配置类的。现在我们要来学习ClassPathBeanDefinitionScanner,首先从ClassPathBeanDefinitionScanner的名字我们大概可以知道,这个类是用来扫描BeanDefinition的类路径的,那么,我们要怎么使用这个类来扫描类路径呢?来看下面的测试用例:
@Test
public void test04() {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
ac.scan("org.example.service");
ac.refresh();
System.out.println(ac.getBean("orderService"));
}运行结果:
org.example.service.OrderService@4ba2ca36上面的测试用例,我们不再像之前在配置类上用@ComponentScan标记要扫描的类路径,并将配置类作为参数传给AnnotationConfigApplicationContext创建对象。而是在调用AnnotationConfigApplicationContext无参构造方法创建对象后,再调用ac.scan(String... basePackages)将类路径传入,而ac.scan(String... basePackages)方法也是调用scanner.scan(String... basePackages)方法来完成类的扫描。
public class AnnotationConfigApplicationContext extends GenericApplicationContext implements AnnotationConfigRegistry {
……
private final ClassPathBeanDefinitionScanner scanner;
……
@Override
public void scan(String... basePackages) {
Assert.notEmpty(basePackages, "At least one base package must be specified");
this.scanner.scan(basePackages);
}
……
}<1>和<2>两块代码最终效果看起来一样,都能扫描我们设定的路径,根据类生成BeanDefinition再生成bean,可能有人会怀疑,扫描用@ComponentScan所标记的类路径,是否是AnnotationConfigApplicationContext的scanner对象? 这里笔者可以告诉大家:扫描@ComponentScan标记的类路径需要用到ClassPathBeanDefinitionScanner类,但并非用AnnotationConfigApplicationContext的scanner对象,而是在代码的某处创建了ClassPathBeanDefinitionScanner对象再调用scan(String... basePackages)方法扫描@ComponentScan标记的路径,至于是哪里创建新的ClassPathBeanDefinitionScanner对象再扫描@ComponentScan标记的路径后面会讲,只是这里我们要知道AnnotationConfigApplicationContext的scanner对象仅仅用来帮助我们添加扫描路径,而实际的开发中很少用到。
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(MyConfig.class);//<1>
————————————————————————————————————————————————————————————————————————————————————————————————
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();//<2>
ac.scan("org.example.service");
ac.refresh();下面,我们来看看ClassPathBeanDefinitionScanner的scan(String... basePackages)方法都做了些什么:
public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateComponentProvider {
private final BeanDefinitionRegistry registry;
……
public int scan(String... basePackages) {
int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
doScan(basePackages);//<1>
// Register annotation config processors, if necessary.
if (this.includeAnnotationConfig) {
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);//<2>
}
return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
}
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) {
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);//<3>
for (BeanDefinition candidate : candidates) {
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);//<4>
if (candidate instanceof AbstractBeanDefinition) {
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
}
if (candidate instanceof AnnotatedBeanDefinition) {
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);//<5>
}
if (checkCandidate(beanName, candidate)) {
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);//<6>
definitionHolder =
AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
registerBeanDefinition(definitionHolder, this.registry);//<7>
}
}
}
return beanDefinitions;
}
……
}从上面的代码我们得知,spring扫描类路径是调用ClassPathScanningCandidateComponentProvider.findCandidateComponents(String basePackage)方法,所以我们继续追踪到这个方法:
public Set<BeanDefinition> findCandidateComponents(String basePackage) {
if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
}
else {
return scanCandidateComponents(basePackage);//<1>
}
}
private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
Set<BeanDefinition> candidates = new LinkedHashSet<>();
try {
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
resolveBasePackage(basePackage) + '/' + this.resourcePattern;//<2>
Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);//<3>
……
for (Resource resource : resources) {
if (resource.isReadable()) {
try {
MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);//<4>
if (isCandidateComponent(metadataReader)) {//<5>
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);//<6>
sbd.setSource(resource);//<7>
if (isCandidateComponent(sbd)) {//<8>
candidates.add(sbd);
}
}
……
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to read candidate component class: " + resource, ex);
}
}
}
}
catch (IOException ex) {
throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
}
return candidates;
}现在,我们来调试一下上面的代码,看看packageSearchPath和resources的内容,首先我们来看我们类路径下的文件:
D:\F\java_space\spring-source\spring-beanFactoryPostProcessor\target\classes\org\example\service 的目录
2020/11/19 08:22 <DIR> .
2020/11/19 08:22 <DIR> ..
2020/11/19 08:22 490 HelloService$BarService.class
2020/11/19 08:22 609 HelloService$FooService.class
2020/11/19 08:22 318 HelloService$Hello.class
2020/11/19 08:22 524 HelloService.class
2020/11/19 08:22 559 OrderService.class
2020/11/19 08:22 1,783 Test1BeanFactoryPostProcessor.class
2020/11/19 08:22 555 UserService.class
7 个文件 4,838 字节
2 个目录 102,708,711,424 可用字节然后调试进入上面的代码,可以看到packageSearchPath的内容为:classpath*:org/example/service/**/*.class,之前说过,这里spring会自定义表达式,通过表达式可以扫描这个路径下的类文件。

现在我们来看下ClassPathScanningCandidateComponentProvider的isCandidateComponent(MetadataReader metadataReader)和isCandidateComponent(AnnotatedBeanDefinition beanDefinition)两个重载方法的实现:
public class ClassPathScanningCandidateComponentProvider implements EnvironmentCapable, ResourceLoaderAware {
……
private final List<TypeFilter> includeFilters = new LinkedList<>();
private final List<TypeFilter> excludeFilters = new LinkedList<>();
……
protected void registerDefaultFilters() {
this.includeFilters.add(new AnnotationTypeFilter(Component.class));//<1>
ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
try {
this.includeFilters.add(new AnnotationTypeFilter(
((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));
logger.trace("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.trace("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");
}
catch (ClassNotFoundException ex) {
// JSR-330 API not available - simply skip.
}
}
……
protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
for (TypeFilter tf : this.excludeFilters) {//<2>
if (tf.match(metadataReader, getMetadataReaderFactory())) {
return false;
}
}
for (TypeFilter tf : this.includeFilters) {//<3>
if (tf.match(metadataReader, getMetadataReaderFactory())) {
return isConditionMatch(metadataReader);
}
}
return false;
}
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
AnnotationMetadata metadata = beanDefinition.getMetadata();
return (metadata.isIndependent() && (metadata.isConcrete() ||
(metadata.isAbstract() && metadata.hasAnnotatedMethods(Lookup.class.getName()))));//<4>
}
……
}现在,我们来分析下下面的类哪些可以成为候选组件。
package org.example.service;
import org.springframework.stereotype.Component;
public class HelloService {
@Component
public class FooService {
}
@Component
public static class BarService {
}
@Component
public interface Hello {
void sayHello();
}
}现在,我们回到AnnotationConfigApplicationContext(Class<?>... componentClasses)的构造方法:
public class AnnotationConfigApplicationContext extends GenericApplicationContext implements AnnotationConfigRegistry {
private final AnnotatedBeanDefinitionReader reader;
private final ClassPathBeanDefinitionScanner scanner;
……
public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
this();//<1>
register(componentClasses);//<2>
refresh();
}
……
@Override
public void register(Class<?>... componentClasses) {
Assert.notEmpty(componentClasses, "At least one component class must be specified");
this.reader.register(componentClasses);//<3>
}
……
}在<1>处,会初始化reader和scanner,我们可以调用AnnotationConfigApplicationContext.scan(String... basePackages)传入类路径,这个方法会进而根据传入的类路径用scanner来扫描组件。当然,我们一般不用这种方式,而是在创建AnnotationConfigApplicationContext对象时将配置类传入,让spring自行读取配置类里的类路径。之前笔者已经大致讲解完<1>处this()方法,现在我们要来学习<2>处的配置类注册方法,如我们所见,<2>处会进而将配置类传给<3>处的reader对象。reader对象会根据配置类生成对应的BeanDefinition注册进spring容器。
AnnotatedBeanDefinitionReader.register(Class<?>... componentClasses)经过一系列的调用,会来到下面的AnnotatedBeanDefinitionReader.doRegisterBean(...)方法,这里我们又看到一个BeanDefinition的实现——AnnotatedGenericBeanDefinition,这段代码在<1>处将配置类生成一个对应的AnnotatedGenericBeanDefinition,在<2>处生成beanName,在<3>处将beanName和BeanDefinition包装成一个BeanDefinitionHolder对象,最后在<4>处将beanName和BeanDefinition注册进原先的AnnotationConfigApplicationContext对象。
public class AnnotatedBeanDefinitionReader {
……
private <T> void doRegisterBean(Class<T> beanClass, @Nullable String name,
@Nullable Class<? extends Annotation>[] qualifiers, @Nullable Supplier<T> supplier,
@Nullable BeanDefinitionCustomizer[] customizers) {
AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass);//<1>
……
abd.setScope(scopeMetadata.getScopeName());
String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));//<2>
……
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);//<3>
definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);//<4>
}
……
}在生成配置类对应的BeanDefinition并注册进spring容器后,AnnotationConfigApplicationContext就会调用父类的refresh()方法,我们先大致看一下refresh()方法:、
public abstract class AbstractApplicationContext extends DefaultResourceLoader
implements ConfigurableApplicationContext {
……
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
……
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();//<1>
……
try {
……
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);//<2>
……
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);//<3>
……
}
catch (BeansException ex) {
……
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
……
}
……
}
}
}代码<1>处会返回AnnotationConfigApplicationContext父类GenericApplicationContext的beanFactory属性,其类型为DefaultListableBeanFactory。之后将beanFactory传给<2>处和<3>处的方法,从方法注释可以看到,<2>处会调用bean工厂后置处理器,<3>处会用beanFactory来初始化剩余的非懒加载单例对象,即我们编写的dao、service、controller……。