https://www.youtube.com/live/fg3pSIfyL6Q?feature=share
В Spring есть аннотация, которая называется @Component
. Эта аннотация говорит фреймворку, что данный класс является служебным компонентом и должен быть загружен в рантайм. Давайте посмотрим, как это происходит. Вот сама аннотация:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Indexed
public @interface Component {
String value() default "";
}
Ссылка на аннотацию на GitHub: https://github.com/spring-projects/spring-framework/blob/4786e2bf53a3f882c10e25d7ff79a18ff47b5e51/spring-context/src/main/java/org/springframework/stereotype/Component.java
Здесь нам всё уже знакомо, кроме аннотации @Indexed
– это аннотация из Spring, которая позволяет записывать индекс классов.
Далее, давайте посмотрим на часть кода, в которой @Component
непосредственно обрабатывается. Например, вот эта часть из класса обработки конфигураций:
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
throws IOException {
//....
if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
// Recursively process any member (nested) classes first
processMemberClasses(configClass, sourceClass, filter);
}
//.....
private void processMemberClasses(ConfigurationClass configClass, SourceClass sourceClass,
Predicate<String> filter) throws IOException {
Collection<SourceClass> memberClasses = sourceClass.getMemberClasses();
if (!memberClasses.isEmpty()) {
List<SourceClass> candidates = new ArrayList<>(memberClasses.size());
for (SourceClass memberClass : memberClasses) {
if (ConfigurationClassUtils.isConfigurationCandidate(memberClass.getMetadata()) &&
!memberClass.getMetadata().getClassName().equals(configClass.getMetadata().getClassName())) {
candidates.add(memberClass);
}
}
OrderComparator.sort(candidates);
for (SourceClass candidate : candidates) {
if (this.importStack.contains(configClass)) {
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
}
else {
this.importStack.push(configClass);
try {
processConfigurationClass(candidate.asConfigClass(configClass), filter);
}
finally {
this.importStack.pop();
}
}
}
}
}
protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException {
if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
return;
}
ConfigurationClass existingClass = this.configurationClasses.get(configClass);
if (existingClass != null) {
if (configClass.isImported()) {
if (existingClass.isImported()) {
existingClass.mergeImportedBy(configClass);
}
// Otherwise ignore new imported config class; existing non-imported class overrides it.
return;
}
else {
// Explicit bean definition found, probably replacing an import.
// Let's remove the old one and go with the new one.
this.configurationClasses.remove(configClass);
this.knownSuperclasses.values().removeIf(configClass::equals);
}
}
// Recursively process the configuration class and its superclass hierarchy.
SourceClass sourceClass = asSourceClass(configClass, filter);
do {
sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
}
while (sourceClass != null);
this.configurationClasses.put(configClass, configClass);
}
Ссылка на класс на GitHub:
https://github.com/spring-projects/spring-framework/blob/4786e2bf53a3f882c10e25d7ff79a18ff47b5e51/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java#L265
В этой части кода Spring проверяет если класс является компонентом, то его нужно положить в коллекцию configurationClasses. Что там происходит дальше немного за рамками нашего курса, здесь мы видим как просто обрабатывается существующая кастомная аннотация.
Spring работает с аннотациями через байт-код с помощью библиотеки ASM: (https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/asm/package-summary.html Это все та же рефлексия, но работает она значительно быстрее, чем с использованием внутренних классов Java.
Код урока находится в прикреплённом архиве. Архив содержит полный проект IDEA, который можно открыть через командное меню File→Open и указанием пути на текущий проект:
.