Spring 如何使用@ComponentScan(“包路径”) 注解
在使用Spring 注解方式开发的开始时候都会创建一个AnnotationConfigApplicationContext 对象,Xml方式开发则会创建一个ClassPathXmlApplicationContext对象,今天讲解的是注解方式的扫描包路径
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
在AnnotationConfigApplicationContext后面都会添加一个配置类
例如:上面的AppConfig.class
在配置类中都会看到一个@ComponentScan(“指定路径”)注解 ,Spring是如何通过该注解进行扫描指定路径?接下来一一解析
AnnotationConfigApplicationContext 构造方法中有三个方法,其中扫描就是在refresh方法中实现的
public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
// 1. 父类创建BeanFactory
// 2. 生成AnnotatedBeanDefinitionReader
// 3. 生成ClassPathBeanDefinitionScanner
this();
// 利用reader把componentClasses注册为一个BeanDefinition
register(componentClasses);
// 调用AbstractApplicationContext的refresh()方法,模板模式,会启动ApplicationContext
// 为什么叫refresh,而不叫start?
refresh();
}
构造方法中在new AnnotatedBeanDefinitionReader 中会添加一个org.springframework.context.annotation.internalConfigurationAnnotationProcessor 这个就是 ConfigurationClassPostProcessor的别名,ConfigurationClassPostProcessor可以理解为一个插件,提前准备这个类,在refresh 方法中可以得到使用
public AnnotationConfigApplicationContext() {
// 在执行这个构造方法之前,会先执行父类的构造方法,会初始化一个beanFactory = new DefaultListableBeanFactory()
// 生成并注册5个BeanDefinition
// 1.ConfigurationClassPostProcessor
// 2.AutowiredAnnotationBeanPostProcessor
// 3.CommonAnnotationBeanPostProcessor
// 4.EventListenerMethodProcessor
// 5.DefaultEventListenerFactory
this.reader = new AnnotatedBeanDefinitionReader(this);
// 注册默认的includeFilter
this.scanner = new ClassPathBeanDefinitionScanner(this);
}
refresh 这个包含了Spring启动的全部流程,今天只解析扫描相关的代码,其他的代码后续更新再说
注重解释一下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.
// 刷新BeanFactory,得到一个空的BeanFactory-Default
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // co
// Prepare the bean factory for use in this context.
// 准备BeanFactory
// 1. 设置BeanFactory的类加载器、表达式解析器、类型转化注册器
// 2. 添加三个BeanPostProcessor,注意是具体的BeanPostProcessor实例对象
// 3. 记录ignoreDependencyInterface
// 4. 记录ResolvableDependency
// 5. 添加三个单例Bean
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
// 子类可以对BeanFactory进行进一步初始化
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
// BeanFactory准备好了之后,执行BeanFactoryPostProcessor,开始对BeanFactory进行处理
// 默认情况下:
// 此时beanFactory的beanDefinitionMap中有6个BeanDefinition,5个基础BeanDefinition+AppConfig的BeanDefinition
// 而这6个中只有一个BeanFactoryPostProcessor:ConfigurationClassPostProcessor
// 这里会执行ConfigurationClassPostProcessor进行@Component的扫描,扫描得到BeanDefinition,并注册到beanFactory中
// 注意:扫描的过程中可能又会扫描出其他的BeanFactoryPostProcessor,那么这些BeanFactoryPostProcessor也得在这一步执行
invokeBeanFactoryPostProcessors(beanFactory); //BeanDefinitionRegistryPostProcessor ,BeanFactoryPostProcessors
// Register bean processors that intercept bean creation.
// 从BeanFactory找出扫描得到得BeanPostProcessor,实例化并注册到BeanFactory中
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
// 初始化MessageSource,如果配置了一个名字叫做“messageSource”的BeanDefinition
// 就会把这个Bean创建出来,并赋值给ApplicationContext的messageSource属性
// 这样ApplicationContext就可以使用国际化的功能了
initMessageSource();
// Initialize event multicaster for this context.
// 设置ApplicationContext的applicationEventMulticaster
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
// 执行子类的onRefresh方法
onRefresh();
// Check for listener beans and register them.
// 注册Listener
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
// 完成beanFactory的初始化(实例化非懒加载的单例bean)
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
// 发布事件
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
invokeBeanFactoryPostProcessors方法中的invokeBeanFactoryPostProcessors执行bean工厂后置处理器
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
//执行bean工厂后置处理器
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
// Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime
// (e.g. through an @Bean method registered by ConfigurationClassPostProcessor)
// 关于LoadTimeWeaver看这篇文章了解即可,
if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
}
}
下面的invokeBeanFactoryPostProcessors只抽取了代码的一部分,首先会通过org.springframework.context.annotation.internalConfigurationAnnotationProcessor别名,再通过beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class)找到ConfigurationClassPostProcessor,然后在invokeBeanDefinitionRegistryPostProcessors中回调ConfigurationClassPostProcessor 的 postProcessBeanDefinitionRegistry方法
public static void invokeBeanFactoryPostProcessors(
ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
// 首先,先执行实现了Ordered接口的BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry方法
postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
processConfigBeanDefinitions 方法准备开始扫描
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
int registryId = System.identityHashCode(registry);
if (this.registriesPostProcessed.contains(registryId)) {
throw new IllegalStateException(
"postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);
}
if (this.factoriesPostProcessed.contains(registryId)) {
throw new IllegalStateException(
"postProcessBeanFactory already called on this post-processor against " + registry);
}
this.registriesPostProcessed.add(registryId);
// 寻找配置类对应的BeanDefinition然后进行处理
// 比如开始扫描
processConfigBeanDefinitions(registry);
}
后面的代码会解析ComponentScans 注解,获得路径,然后根据路径进行扫描
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
向下跟代码会跟到一个return scanner.doScan(StringUtils.toStringArray(basePackages)); 这才是真正的扫描
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
// 这是Spring中的Assert,大家开发时也可以用
Assert.notEmpty(basePackages, "At least one base package must be specified");
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
for (String basePackage : basePackages) {
// 扫描包路径得到BeanDefinition,得到的BeanDefinition是空的,还没有解析类上所定义的注解信息
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
for (BeanDefinition candidate : candidates) {
// 得到Scope的信息,并设置
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());
// 得到beanName
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
if (candidate instanceof AbstractBeanDefinition) {
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
}
if (candidate instanceof AnnotatedBeanDefinition) {
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}
if (checkCandidate(beanName, candidate)) {
// 生成BeanDefinitionHolder并注册到registry中
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
definitionHolder =
AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}
findCandidateComponents(basePackage); 中addCandidateComponentsFromIndex方法中有一个支持快速扫描的机制由Spring 提供 ,在META-INF/spring.components创建一个这样的文件配置如下:
左侧为要扫描的类 右侧为相对应的注解,这样Spring 会直接扫描该文件
example.scannable.DefaultNamedComponent=org.springframework.stereotype.Component
example.scannable.NamedComponent=org.springframework.stereotype.Component
example.scannable.StubFooDao=org.springframework.stereotype.Component
example.scannable.NamedStubDao=org.springframework.stereotype.Component
example.scannable.ServiceInvocationCounter=org.springframework.stereotype.Component
example.scannable.sub.BarComponent=org.springframework.stereotype.Component
代码流程图
AnnotationConfigApplicationContext
-> this();
-> this.reader = new AnnotatedBeanDefinitionReader(this)(生成并注册5个BeanDefinition其中有一个ConfigurationClassPostProcessor)
->refresh()
->invokeBeanFactoryPostProcessors(beanFactory);
->PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
->invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
->postProcessor.postProcessBeanDefinitionRegistry(registry);
->ConfigurationClassPostProcessor中的postProcessBeanDefinitionRegistry方法
->processConfigBeanDefinitions(registry);
->parser.parse(candidates);
->parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
->processConfigurationClass(new ConfigurationClass(metadata, beanName));
->sourceClass = doProcessConfigurationClass(configClass, sourceClass);
->this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
->scanner.doScan(StringUtils.toStringArray(basePackages));