Знания меняют судьбу, программирование делает меня счастливым, и я продолжу шагать в мире открытого исходного кода в 2020 году.
Ставьте лайк и смотрите снова, формируйте привычку
дай мне звезду,Нажмите, чтобы узнать о решении для посадки службы компонентного интерфейса на основе SpringBoot.
мы использовалиSpringBoot
для разработки приложений, но почему аннотации автоматически регистрируются при старте проекта@Component
,@Service
,@RestController
... помеченыBean
Шерстяная ткань?
Рекомендуемое чтение
Каталог сканирования по умолчанию
SpringBoot
поместите начальный класс, гдеPackage
В качестве каталога сканирования по умолчанию это также является ограничением, если мы поместим необходимость регистрации вIOC
Класс, созданный в каталоге сканирования, может быть зарегистрирован автоматически, иначе он не будет зарегистрирован.
Если ваш начальный класс называетсяExampleApplication
,Это расположеноorg.minbox.chapter
каталог, он будет автоматически сканироваться, когда мы запускаем приложениеorg.minbox.chapter
одноуровневый каталог,подкаталогВсе аннотированные классы выглядят следующим образом:
. src/main/java
├── org.minbox.chapter
│ ├── ExampleApplication.java
│ ├── HelloController.java
│ ├── HelloExample.java
│ └── index
│ │ └── IndexController.java
├── com.hengboy
│ ├── TestController.java
└──
HelloController.java
,HelloExample.java
с начальным классомExampleApplication.java
В каталоге того же уровня, поэтому при запуске проектаможно сканировать в.
IndexController.java
Он находится в подкаталоге начального классаorg.minbox.chapter.index
, так как он поддерживает сканирование каталогов нижнего уровня, он такжеможно сканировать в.
TestController.java
родыcom.hengboy
Под содержанием,Невозможно сканировать по умолчанию.
Пользовательский каталог сканирования
Расположен в вышеуказанной структуре каталоговcom.hengboy
в каталогеTestController.java
класс, который по умолчанию не может быть просканирован и зарегистрирован вIOC
В контейнере, если вы хотите сканировать классы в этом каталоге, есть два метода ниже.
Способ 1: аннотировать с помощью @ComponentScan
@ComponentScan({"org.minbox.chapter", "com.hengboy"})
Способ 2. Используйте свойство scanBasePackages.
@SpringBootApplication(scanBasePackages = {"org.minbox.chapter", "com.hengboy"})
Примечание. После настройки пользовательского каталога сканированияпереопределит каталог сканирования по умолчанию, если вам также необходимо сканировать каталог по умолчанию, вам необходимо настроить каталог сканирования.В приведенной выше пользовательской конфигурации, если вы настраиваете только сканирование
com.hengboy
каталог, затемorg.minbox.chapter
Каталог не будет сканироваться.
отследить исходный код
Давайте взглянемSpringBoot
Как исходный код реализует автоматическое сканирование по каталогуBean
, и воляBean
Процесс регистрации в контейнере.
Поскольку процесс регистрации относительно сложен, для объяснения выбраны репрезентативные этапы процесса.
GetBasePackages
существуетorg.springframework.context.annotation.ComponentScanAnnotationParser#parse
получить методbasePackages
Бизнес-логика исходного кода выглядит следующим образом:
Set<String> basePackages = new LinkedHashSet<>();
// 获取@ComponentScan注解配置的basePackages属性值
String[] basePackagesArray = componentScan.getStringArray("basePackages");
// 将basePackages属性值加入Set集合内
for (String pkg : basePackagesArray) {
String[] tokenized = StringUtils.tokenizeToStringArray(this.environment.resolvePlaceholders(pkg),
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
Collections.addAll(basePackages, tokenized);
}
// 获取@ComponentScan注解的basePackageClasses属性值
for (Class<?> clazz : componentScan.getClassArray("basePackageClasses")) {
// 获取basePackageClasses所在的package并加入Set集合内
basePackages.add(ClassUtils.getPackageName(clazz));
}
// 如果并没有配置@ComponentScan的basePackages、basePackageClasses属性值
if (basePackages.isEmpty()) {
// 使用Application入口类的package作为basePackage
basePackages.add(ClassUtils.getPackageName(declaringClass));
}
ПолучатьbasePackages
Он делится на три этапа, а именно:
- Получать
@ComponentScan
аннотацияbasePackages
значение атрибута - Получать
@ComponentScan
аннотацияbasePackageClasses
значение атрибута - Буду
Application
где находится начальный классpackage
по умолчаниюbasePackages
Примечание. Согласно исходному коду подтверждается, почему мы настроили
basePackages
,basePackageClasses
После этого будет перезаписано значение по умолчанию.На самом деле здесь не перезапись.Он не будет получен вообще.Application
начальный классpackage
.
Сканировать Bean-компоненты в пакетах
получить всеPackages
После этого черезorg.springframework.context.annotation.ClassPathBeanDefinitionScanner#doScan
способ сканирования каждогоPackage
Используя регистрационную аннотацию под (@Component
,@Service
,@RestController
...) помеченный класс, исходный код выглядит следующим образом:
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
// 当basePackages为空时抛出IllegalArgumentException异常
Assert.notEmpty(basePackages, "At least one base package must be specified");
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
// 遍历每一个basePackage,扫描package下的全部Bean
for (String basePackage : basePackages) {
// 获取扫描到的全部Bean
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
// 遍历每一个Bean进行处理注册相关事宜
for (BeanDefinition candidate : candidates) {
// 获取作用域的元数据
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());
// 获取Bean的Name
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
if (candidate instanceof AbstractBeanDefinition) {
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
}
// 如果是注解方式注册的Bean
if (candidate instanceof AnnotatedBeanDefinition) {
// 处理Bean上的注解属性,相应的设置到BeanDefinition(AnnotatedBeanDefinition)类内字段
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}
// 检查是否满足注册的条件
if (checkCandidate(beanName, candidate)) {
// 声明Bean具备的基本属性
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
// 应用作用域代理模式
definitionHolder =
AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
// 写入返回的集合
beanDefinitions.add(definitionHolder);
// 注册Bean
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}
В приведенном выше исходном коде будет сканироваться каждыйbasePackage
определяется аннотациямиBean
,ПолучатьBean
После регистрации объекта определения и установки некоторых основных свойств.
Зарегистрировать Бин
сканировать вbasePackage
внизBean
непосредственно черезorg.springframework.beans.factory.support.BeanDefinitionReaderUtils#registerBeanDefinition
способ регистрации, исходный код выглядит следующим образом:
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
// 注册Bean的唯一名称
String beanName = definitionHolder.getBeanName();
// 通过BeanDefinitionRegistry注册器进行注册Bean
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
// 如果存在别名,进行注册Bean的别名
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
}
пройти черезorg.springframework.beans.factory.support.BeanDefinitionRegistry#registerBeanDefinition
Методы в регистраторе могут напрямуюBean
зарегистрироваться наIOC
внутри контейнера иBeanName
Это уникальное имя при жизни.
Суммировать
Благодаря объяснению этой статьи, я думаю, вы уже должны понятьSpringBoot
Почему приложение автоматически сканирует при запускеpackage
и воляBean
зарегистрироваться наIOC
В контейнере, хотя время запуска проекта очень короткое, это очень сложный процесс, и вы можете использовать его в процессе обучения.Debug
режим, чтобы увидеть логическую обработку каждого шага.
автор личноблог
Используйте фреймворк с открытым исходным кодомApiBootПомочь вам стать архитектором сервисов интерфейса API