Springboot启动扫描包的原理
参考链接1
参考链接2
所参照代码为Springboot2.1.1
默认情况下,扫描范围是主类xxxApplication所在包及其子目录,可以在后面的具体实现中看到。
从主类中的SpringApplication.run(xxxApplication.class, args);
一直点击进入run方法的实现,这里可以看到run方法里有几个关于context的方法分别是:
-
createApplicationContext()
-
prepareContext(xxx,xx)
-
refreshContext(context)
public ConfigurableApplicationContext run(String... args) {
...
try {
...
context = createApplicationContext();
...
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
refreshContext(context);
....
catch (Throwable ex) {
}
逐个分析:
1.createApplicationContext()
这个方法返回一个类型为AnnotationConfigServletWebServerApplicationContext
的context,可以点进去看到这个class为AnnotationConfigServletWebServerApplicationContext
类型。
protected ConfigurableApplicationContext createApplicationContext() {
...
case SERVLET:
contextClass = Class.forName(**DEFAULT_SERVLET_WEB_CONTEXT_CLASS**);
break;
case ...
}
}
....
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
该类的一个父类GenericApplicationContext
中,创建了一个beanFactory
,这个beanFactory实现了BeanDefinitionRegistry
的接口,后面会用到。
public GenericApplicationContext() {
this.beanFactory = new DefaultListableBeanFactory();
}
createApplicationContext返回的语句中调用了AnnotationConfigServletWebServerApplicationContext
类的构造函数,
public AnnotationConfigServletWebServerApplicationContext() {
this.reader = new AnnotatedBeanDefinitionReader(this);
this.scanner = new ClassPathBeanDefinitionScanner(this);
}
进入构造函数中的第一个AnnotatedBeanDefinitionReader
,一直点到registerAnnotationConfigProcessors
的方法:
public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
BeanDefinitionRegistry registry, @Nullable Object source) {
//创建了一个beanFactory
DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry);
if (beanFactory != null) {
.....
}
Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8);
if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
def.setSource(source);
//添加了一个类型为CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME的beanDefinition,下面一段代码可以看到它的名字为internalConfigurationAnnotationProcessor,(ConfigurationClassPostProcessor.class)为类型的beanDefinition 这个后面会用到
beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
}
....
}
public static final String CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME =
"org.springframework.context.annotation.internalConfigurationAnnotationProcessor";
接着进入到构造函数中的new ClassPathBeanDefinitionScanner(),
public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,
Environment environment, @Nullable ResourceLoader resourceLoader) {
....
//入口处默认useDefaultFilters为true
if (useDefaultFilters) {
registerDefaultFilters();
}
setEnvironment(environment);
setResourceLoader(resourceLoader);
}
进入registerDefaultFilters
方法,
protected void registerDefaultFilters() {
//这里把Component.class作为默认的includeFilter加入了过滤器
this.includeFilters.add(new AnnotationTypeFilter(Component.class));
ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
try {
......
}
2.prepareContext()
prepareContext()方法里有个load函数,进入load函数的具体实现,
private int load(Object source) {
Assert.notNull(source, "Source must not be null");
if (source instanceof Class<?>) {
return load((Class<?>) source);
}
if (source instanceof Resource) {
return load((Resource) source);
}
if (source instanceof Package) {
....
}
因为主类的run方法传入的是xxxxApplication.class,所以这里走第一个分支
private int load(Class<?> source) {
.....
//isComponent里面判断是否有Component的注解,启动类是有的,所以这里将启动类加载进来了
if (isComponent(source)) {
this.annotatedReader.register(source);
return 1;
}
return 0;
}
3.refreshContext()
点击进入refresh的具体实现,看到一个invokeBeanFactoryPostProcessors的方法
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
//这里写着注册bean
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
....
进入其实现,这里传入的beanFactory的就是1.createApplicationContext()中第一个方法创建的的beanFactory
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
.....
}
进入这个方法,因为1.createApplicationContext中创建的beanFactory实现了BeanDefinitionRegistry接口,所以我们进入这个分支看一看,
public static void invokeBeanFactoryPostProcessors(
ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
Set<String> processedBeans = new HashSet<>();
//BeanDefinitionRegistry
if (beanFactory instanceof BeanDefinitionRegistry) {
.....
for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
//BeanDefinitionRegistryPostProcessor
if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
BeanDefinitionRegistryPostProcessor registryProcessor =
(BeanDefinitionRegistryPostProcessor) postProcessor;
registryProcessor.postProcessBeanDefinitionRegistry(registry);
.....
}
还是1.createApplicationContext()中创建的ConfigurationClassPostProcessor类,刚好实现了BeanDefinitionRegistryPostProcessor,所以这里我们直接看ConfigurationClassPostProcessor累的postProcessBeanDefinitionRegistry方法,可以看到这里调用了processConfigBeanDefinitions方法,
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
....
processConfigBeanDefinitions(registry);
}
进入这个方法:
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
.....
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
do {
parser.parse(candidates);
parser.validate();
.....
}
进入parse(candidates),这里再前面创建ConfigurationClassPostProcessor的时候给它生成了一个Holder,而这个beanDefinition的类型就是AnnotatedBeanDefinition,所以走这个分支的parse方法
public void parse(Set<BeanDefinitionHolder> configCandidates) {
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
try {
//这个分支
if (bd instanceof AnnotatedBeanDefinition) {
parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
}
else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
....
}
一路点下去,进入doProcessConfigurationClass方法,发现这里就是用ComponentScan来解析bean对象的
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
throws IOException {
.....
// Process any @ComponentScan annotations
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
if (!componentScans.isEmpty() &&
!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
for (AnnotationAttributes componentScan : componentScans) {
// 进入这个方法
Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
}
}
在这个parse方法的最后,可以看到basePackages.isEmpty()的判断,当前这个basePackages就是空的,因为没有配置@ComponentScan的这个属性,这里就会给basePackages添加这个启动类的名字的最后一个点之前的路径,比如启动类是A.B.C.D.class, 那么路径就是A.B.C.D,加入它所在的包名
public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {
....
if (basePackages.isEmpty()) {
basePackages.add(ClassUtils.getPackageName(declaringClass));
}
scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) {
@Override
protected boolean matchClassName(String className) {
return declaringClass.equals(className);
}
});
return scanner.doScan(StringUtils.toStringArray(basePackages));
}
最后进入doScan方法,这里有个findCandidateComponents的方法,得到这些候选的bean就会注册到Spring容器中。
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
....
for (String basePackage : basePackages) {
//这个方法
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
for (BeanDefinition candidate : candidates) {
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
....
return beanDefinitions;
}
最后进入scanCandidateComponents方法,这里Spring会通过传入的路径来遍历下面的每一个class,这个方法所在类为ClassPathScanningCandidateComponentProvider,不过所有的basePackage上面那个方法(所在类ClassPathBeanDefinitionScanner)打断点查看。
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);
//这里判断了是否是Component.class
if (isCandidateComponent(metadataReader)) {
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
sbd.setResource(resource);
sbd.setSource(resource);
if (isCandidateComponent(sbd)) {
.....
}
由于上面提到过的默认的includeFilters中加入了Componet.class,所以在启动类的同级目录下,添加Component注解及其子注解的类会被加入到Spring容器中。
/**
* Determine whether the given class does not match any exclude filter
* and does match at least one include filter.
* @param metadataReader the ASM ClassReader for the class
* @return whether the class qualifies as a candidate component
*/
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;
}
结论
最终实现类路径组件扫描的是ClassPathScanningCandidateComponentProvider类下的scanCandidateComponents方法,查看全部扫描包可以在它的上级调用方法ClassPathBeanDefinitionScanner类的doScan(String... basePackages)方法打断点查看。