Class ComponentScanner
-
Field Summary
Fields -
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionprivate StringgetBeanName(Class<?> clazz) Get bean name from class.private booleanhasComponentAnnotation(Class<?> clazz) Check if class has a meta-component annotation, anywhere on its whole annotation tree.private booleanisComponent(Class<?> clazz) Check if class is a component.private voidprocessBeanMethod(Object configInstance, Method method) Process @Bean method.private voidprocessClass(Class<?> clazz) Process a class for component annotations.private voidregisterComponent(Class<?> clazz) Register a component class.private voidregisterConfiguration(Class<?> clazz) Register a configuration class.private String[]resolveBeanNames(Method method) Resolve the effective bean-name array for a@Beanmethod (D-06):name()if non-empty, elsevalue()if non-empty, else the method's own name as a single-element array.private voidscanDirectory(File directory, String packageName, ClassLoader classLoader, String moduleName) Scan directory for class files.private voidscanJar(URL resource, String basePackage, ClassLoader classLoader) Scan inside a JAR file for class files.voidscanPackage(String basePackage) Scan a single package for components.voidscanPackages(String... basePackages) Scan packages for components.private booleanshouldRegister(Class<?> clazz) Check if a class should be registered based on @ConditionalOnConfig.
-
Field Details
-
LOGGER
-
container
-
-
Constructor Details
-
ComponentScanner
-
-
Method Details
-
scanPackages
Scan packages for components.- Parameters:
basePackages- packages to scan
-
scanPackage
Scan a single package for components.- Parameters:
basePackage- package to scan
-
scanJar
Scan inside a JAR file for class files. -
scanDirectory
private void scanDirectory(File directory, String packageName, ClassLoader classLoader, String moduleName) Scan directory for class files.- Parameters:
directory- the directory to scanpackageName- the dotted package name fordirectory, growing with each recursive descent into a subdirectoryclassLoader- the class loader to load discovered classes withmoduleName- the D-19 diagnostic identifier for this scan -- fixed at the originalbasePackagefor the whole recursion, deliberately NOT the per-recursionpackageNameabove, so a class three subdirectories deep is still attributed to the same modulescanPackage'sfinallyblock will emit a summary for
-
processClass
Process a class for component annotations.Propagation rule (D-25), stated once here rather than duplicated at each per-class call site in
scanJar/scanDirectory:ContainerExceptionis the single type that propagates unconditionally out of this method and aborts the module's scan -- every other exception thrown while registering an individual class is logged and that one class is skipped, and the rest of the package still registers. One type name is the whole rule, so no allowlist has to be kept in sync. The two deliberateContainerExceptionthrows today are the@Finalcontract violation below and a malformed@AliasFordeclaration surfaced byMergedAnnotationResolverduring the same scan; neither may ever be caught by a blanket handler. -
shouldRegister
Check if a class should be registered based on @ConditionalOnConfig.Delegates to
ConditionalRegistrationEvaluator, the single shared implementation of this decision (D-17). This is the only call site of that evaluator insrc/main(verified:ListenerManagerandCommandManagerhave no package-scan overload that consults it), so this scanner is the one place the annotation needs to be honoured correctly.- Parameters:
clazz- the class to check- Returns:
- true if the class should be registered
-
isComponent
Check if class is a component. -
hasComponentAnnotation
Check if class has a meta-component annotation, anywhere on its whole annotation tree.Widened from the previous hand-written implementation (D-03, 03-02), which inspected only the annotations declared directly on the class and only one meta-annotation level deep, so a stereotype annotation composed two levels above
@Componentwas not recognised.MergedAnnotationResolver.isPresent(java.lang.Class<?>, java.lang.Class<? extends java.lang.annotation.Annotation>)walks the full annotation tree, so it now is. This is intentional -- a composed stereotype annotation exists precisely to be recognised -- and is recorded againstCOMPATIBILITY.md's behaviour-change criterion by 03-10.Exception: a
UltiToolsPluginsubclass is never treated as a component here, regardless of what its annotation tree resolves to.@UltiToolsModulecomposes@Configuration, which is itself meta-annotated@Component-- so an unqualified whole-tree walk would also match every module's own main class. That class is already constructed and registered as a singleton byPluginManager*before* this scan ever runs (its `plugin.yml` metadata has to be read first), under its rawClass.getSimpleName()as the bean name.ComponentScanner's own bean-naming convention decapitalizes that same name, so the two registrations never collide, and treating the module main class as a scannable@Componentwould register a *second* bean definition for it under the decapitalized name -- whichpreInstantiateSingletonswould then construct via reflection, re-running the module's entire no-arg constructor (`plugin.yml` re-parse, resource re-copy, a second config-entity registration under an orphaned plugin instance) a second time. Excluded here rather than inisComponent(java.lang.Class<?>), whose own four-way disjunction stays untouched (D-03 scope). -
registerComponent
Register a component class.For a class carrying
@CmdExecutor, the@CmdTargetclass-versus-method composition is checked here - pure reflection, beforeregisterBeanDefinition, and inside this method's own try/catch - because this is the isolation primitive: a check that lived inprocessClass(no try/catch, aborts the whole scan) or in the executor's own constructor (runs insidepreInstantiateSingletons, no per-bean isolation, would fail the whole plugin) would take down every command in the module instead of just this one class. See T-01-01b in this plan's threat model and D-03. -
registerConfiguration
Register a configuration class. -
processBeanMethod
Process @Bean method.The bean name is derived from
@Bean'sname()/value()(D-06) viaresolveBeanNames(Method), falling back to the method's own name exactly as before this attribute took effect. The first resolved name is registered throughSimpleContainer.registerSingleton(String, Object), which fully assembles the instance (D-14); any remaining names are bound as aliases to that same, already assembled reference via the package-visibleSimpleContainer.addSingleton(String, Object)-- deliberately not a secondregisterSingletoncall per name, which after D-14 would re-run autowiring and@PostConstructonce per alias on what should be one bean. -
resolveBeanNames
Resolve the effective bean-name array for a@Beanmethod (D-06):name()if non-empty, elsevalue()if non-empty, else the method's own name as a single-element array.name()andvalue()are mutual aliases -- declaring both with different content is a malformed declaration and hard-fails naming the method and both declared values, reusing the sameContainerException.malformedAliasFor(java.lang.Class<? extends java.lang.annotation.Annotation>, java.lang.String, java.lang.String)factory (and therefore the same message shape) plan 03-01 built for malformed@AliasFordeclarations, even though@Beanitself deliberately does not declare a real@AliasForbetween its two attributes (see this method's own class's scope boundary). Declaring both with identical content is legal. Every resolved element must be non-blank -- a blank or whitespace-only element is also a malformed declaration, because a name that cannot name anything is not a usable third state between "declared" and "absent" (D-02). No element is normalized: two names differing only by Unicode normalization form are two distinct declared names, decided purely byString.equals.- Parameters:
method- the@Bean-annotated factory method- Returns:
- the resolved name array; index 0 is the registered bean name, the rest are aliases
-
getBeanName
Get bean name from class.
-