Class SimpleContainer
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionstatic enumprivate static final classOne by-type candidate asdescribeCandidates(Class)sees it: a name, the existing instance if there is one, and the class its priority is read from. -
Field Summary
FieldsModifier and TypeFieldDescriptionprivate AopProxyResolverResolves which class to instantiate for a bean.private final Map<String, BeanDefinition> private final List<BeanPostProcessor> private final Map<String, SimpleContainer.BeanScope> private ClassLoaderprivate Stringprivate booleanprivate static final Loggerprivate SimpleContainer -
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionvoidaddBeanPostProcessor(BeanPostProcessor processor) Add bean post processor.protected voidaddSingleton(String beanName, Object singletonObject) Add a fully initialized singleton to level 1 cache.protected voidaddSingletonFactory(String beanName, Supplier<Object> singletonFactory) Add a singleton factory to level 3 cache for early reference support.voidclose()Close the container.booleancontainsBean(String name) Check if container contains bean.private ObjectcreateBean(String name, BeanDefinition definition) Create bean from definition.private ObjectcreateBeanWithConstructorInjection(Class<?> beanClass) Create bean using constructor auto-wiring.private booleandeclaresOwnBeanName(Class<?> type) True ifftypeitself carries an explicit@Component(value = ...)or@Service(value = ...)name -- the same conditiongetBeanName(Class)checks before falling back to a decapitalized-simple-name guess.private List<SimpleContainer.ByTypeCandidate> describeCandidates(Class<?> type) Describes, without constructing anything, every candidate in this container (not the parent) thatgetBean(Class)adjudicates fortype, ordered by@Service(priority)descending (C58, P-09).Each distinct singleton instance in this container once, by reference identity -- the sameIdentityHashMaptechniquegetOrderedBeansOfType(Class)uses, and for the same reason: a bean may overrideequals(), and only the same object reached through two names may collapse (C50).private Constructor<?> findMatchingConstructor(Class<?> beanClass, Object[] args) Find a matching constructor for the given arguments.Gets the AOP proxy resolver for this container.Get autowire capable bean factory.<T> TGet bean by type.Get bean by name.<T> TGet bean by name and type.String[]Get all bean names.Get bean factory.private <T> StringgetBeanName(Class<T> type) Generate bean name for class.String[]getBeanNamesForType(Class<?> type) Get bean names for type.getBeansOfType(Class<T> type) Get beans of type.Get class loader.<T> TgetHighestPriorityBean(Class<T> type) Get the highest priority bean of the given type.<T> List<T> getOrderedBeansOfType(Class<T> type) Get beans of type ordered by @Service priority.private intgetServicePriority(Class<?> clazz) Get service priority from @Service annotation.protected ObjectgetSingleton(String beanName, boolean allowEarlyReference) Get singleton from three-level cache.Get all singleton bean instances registered in this container (not including parent), each distinct instance exactly once.Class<?> Get bean type.booleanhasConstructedInstanceOfType(Class<?> type) Report whether a constructed instance assignable totypealready exists in this container or any parent, without constructing anything.private voidInvalidates the by-type assignability resolution cache (resolvedTypeCache).private voidInvoke methods annotated with @PostConstruct.private voidInvoke methods annotated with @PreDestroy on a bean.private booleanisAssignable(Class<?> paramType, Object value) Check if a value can be assigned to a parameter type.private booleanisPrimitiveWrapperOf(Class<?> primitive, Class<?> wrapper) Check if wrapper is the wrapper class for primitive type.booleanisPrototype(String name) Check if bean is prototype.booleanCheck if container is running.booleanisSingleton(String name) Check if bean is singleton.(package private) booleanmarkConfigurationAssembled(Object configuration) Records that the scan's assembly phase runs the@Beanmethods ofconfiguration(package-visible forComponentScanner).voidInitialize all singletons.voidprocessConfigurationClass(Class<?> configClass) Process configuration class.voidrefresh()Refresh the container.private voidrefuseIfAopAnnotated(Object instance) Refusesinstancewhen its class carries an AOP annotation (@Transactional/@ExceptionCatch) it can never honour (D-15).<T> voidregisterBean(Class<T> type, Object... constructorArgs) Register bean with constructor arguments.voidregisterBeanDefinition(String name, BeanDefinition definition) Register bean definition.voidRegister shutdown hook.voidregisterSingleton(String name, Object instance) Registers a singleton instance, fully assembling it before storage.voidregisterSupplier(String name, Supplier<Object> supplier) Register a supplier for lazy initialization.voidregisterSupplier(String name, Supplier<Object> supplier, Class<?> type) Register a supplier for lazy initialization with type information.<T> voidregisterType(Class<T> type, T instance) Register a type mapping.<T> voidregisterTypeSupplier(Class<T> type, Supplier<T> supplier) Register a type supplier.voidscanComponents(String... basePackages) Scan components in packages.voidsetAopProxyResolver(AopProxyResolver resolver) Sets the AOP proxy resolver for this container.voidsetClassLoader(ClassLoader classLoader) Set class loader.voidsetDisplayName(String displayName) Set display name.voidsetParent(SimpleContainer parent) Set parent container.voidstart()Start the container.voidstop()Stop the container.
-
Field Details
-
LOGGER
-
singletonObjects
-
earlySingletonObjects
-
singletonFactories
-
singletons
-
suppliers
-
typeMappings
-
typeSuppliers
-
resolvedTypeCache
-
beanScopes
-
beanTypes
-
beanPostProcessors
-
beanDefinitions
-
currentlyCreating
-
unresolvableBeans
-
supplierTypes
-
assembledConfigurations
-
parent
-
classLoader
-
isStarted
private boolean isStarted -
displayName
-
aopProxyResolver
Resolves which class to instantiate for a bean. Null means AOP is not wired for this container, in which case every bean is instantiated as its declared class.
-
-
Constructor Details
-
SimpleContainer
public SimpleContainer() -
SimpleContainer
-
-
Method Details
-
getSingleton
Get singleton from three-level cache. This method implements the circular dependency resolution through early singleton exposure.- Parameters:
beanName- bean nameallowEarlyReference- whether to allow early reference (level 2/3 cache)- Returns:
- singleton instance or null if not found
-
addSingletonFactory
Add a singleton factory to level 3 cache for early reference support.- Parameters:
beanName- bean namesingletonFactory- factory for creating early reference
-
addSingleton
Add a fully initialized singleton to level 1 cache. Clears from level 2 and level 3 caches.- Parameters:
beanName- bean namesingletonObject- fully initialized singleton
-
registerSingleton
Registers a singleton instance, fully assembling it before storage.Runs the same
postProcessBeforeInitialization -> autowireBean -> @PostConstruct -> postProcessAfterInitializationsequencecreateBean(java.lang.String, com.ultikits.ultitools.context.BeanDefinition)runs for a container-constructed bean, in that order, before storing -- unconditionally, whether or notrefresh()has been called. Before 6.3.0 this method was two lines:addSingletonplus a type-mapping write, with no autowiring, no@PostConstructinvocation, and noBeanPostProcessorchain -- an object handed in through this method reached the container half-initialised.refresh()only pre-instantiates non-lazy singleton definitions and never touches aregisterSingleton-path object, so gating assembly on refresh state (as a narrower, window-guard fix would) would have left the config entities, the@Configurationinstance, and the@Beanproducts -- all registered beforerefresh()runs -- exactly as uninjected as before (D-14).The bean actually stored, and returned by a later
getBean(String), is whichever referencepostProcessAfterInitializationlast returned -- not necessarilyinstanceitself, if a registeredBeanPostProcessorsubstitutes it.Refuses
instanceoutright when its class carries@Transactionalor@ExceptionCatch-- method-level or class-level -- that it can never honour (D-15). A ByteBuddy proxy is the bean itself in this framework's design; there is no separate delegate target, so a proxy can never be retrofitted onto an object the caller already constructed. Before this refusal existed, the annotation was silently inert: the method ran completely unprotected/untransacted, with no signal anything was wrong. An instance that is already a generated proxy (ProxyFactory.isProxyClass(java.lang.Class<?>)) is exempt -- it already honours its own annotations, copied onto the generated subclass byProxyFactoryitself.No circular-dependency support between two
registerSingleton-registered objects (WR-02). UnlikecreateBean(java.lang.String, com.ultikits.ultitools.context.BeanDefinition), this method does not add an early reference tosingletonFactories/earlySingletonObjectsbefore autowiring -- and, unlikecreateBean, it structurally cannot benefit from doing so.createBean's early exposure works because the container itself triggers a dependency's construction lazily, recursively, inside the same call stack that is still assembling the bean that depends on it -- so an early, not-yet-autowired self-reference can be handed back partway through. AregisterSingletoncaller, by contrast, hands in an object the caller already fully constructed outside the container; the container never constructs it and has no hook to trigger construction of a second, not-yet-registered object on the first one's behalf. Concretely: if object A and object B are registered via two separate, sequentialregisterSingletoncalls and each has an@Autowiredfield pointing at the other, A's autowiring runs while B does not exist in this container in any form yet (no definition, no factory, nothing) -- there is nothing an early-reference mechanism could expose. A's field then either throws (ifrequired = true) or staysnull(ifrequired = false); B, registered second, resolves normally since A already exists by then. This is reachable by an ordinary module author writing two@Beanmethods (or a@Configurationclass and one of its own@Beanproducts) that reference each other, since both paths register their product via this method. Workarounds: order the two registrations so the one with therequired = truefield is registered second, use@Autowired(required = false)plus manual post-registration wiring, or -- where possible -- register the pair through component scanning instead (@Service/@Component), wherecreateBean(java.lang.String, com.ultikits.ultitools.context.BeanDefinition)'s three-level cache does apply.- Parameters:
name- instance nameinstance- instance object- Throws:
ContainerException- ifinstance's class carries an AOP annotation it can never honour (D-15), or if a required@Autowireddependency on it cannot be resolved (D-08)
-
refuseIfAopAnnotated
Refusesinstancewhen its class carries an AOP annotation (@Transactional/@ExceptionCatch) it can never honour (D-15).A generated proxy (
ProxyFactory.isProxyClass(java.lang.Class<?>)) is exempt -- it already honours its own annotations. Otherwise, method-level annotations are read viaAopEligibility.findAopAnnotatedMethods(java.lang.Class<?>), which deliberately omits class-level ones (see its own javadoc), so the class-level half is checked separately here -- omitting it would leave a class-level@Transactionalsilently inert, restating SILENT-10 rather than fixing it.- Parameters:
instance- the instance about to be registered- Throws:
ContainerException- naming the offending class and the annotated method (or"class-level") when a refusal applies
-
registerSupplier
Register a supplier for lazy initialization.- Parameters:
name- supplier namesupplier- supplier function
-
registerSupplier
Register a supplier for lazy initialization with type information.- Parameters:
name- supplier namesupplier- supplier functiontype- the type of bean this supplier produces
-
registerType
Register a type mapping.- Parameters:
type- class typeinstance- instance object
-
registerTypeSupplier
Register a type supplier.- Parameters:
type- class typesupplier- supplier function
-
getBean
Get bean by name.- Parameters:
name- bean name- Returns:
- bean instance
-
getBean
Get bean by type.- Parameters:
type- bean type- Returns:
- bean instance
-
describeCandidates
Describes, without constructing anything, every candidate in this container (not the parent) thatgetBean(Class)adjudicates fortype, ordered by@Service(priority)descending (C58, P-09).- Each singleton instance assignable to
type, once by reference identity -- an object bound under two names (e.g. DependenceManagers' TeleportService, NotificationService and EmailService, each aliased under a short name plus the interface FQN; regression fixed after PR #352's real-machine UAT) is one candidate, while two distinct objects stay two even ifequals(). Its priority is read off its own class, so a proxied bean's priority is read correctly: ProxyFactory copies the target's annotations onto the generated subclass. - Each bean definition assignable to
typewhose name the singleton pass did not see, with the priority of its definition's bean class. Deterministically unresolvable definitions are deliberately not filtered out (07-23): for a sole poisoned implementor, filtering would turn its original error into a null resolution and an uncaught failure for a different bean.
nullor settle on a lower-priority candidate. Such a candidate is described by its new instance (once by identity, as above), and the winner is unchanged. The sort is stable, so candidates of equal priority keep the order above.- Parameters:
type- the requested type- Returns:
- the ordered candidates; empty when this container has none
- Each singleton instance assignable to
-
getBean
Get bean by name and type.- Parameters:
name- bean nametype- bean type- Returns:
- bean instance
-
hasConstructedInstanceOfType
Report whether a constructed instance assignable totypealready exists in this container or any parent, without constructing anything.This exists because
getBeanNamesForType(Class)answers a different question. That method enumerates names, and it draws them from three of the four places an instance can live:singletons,beanDefinitionsandsupplierTypes. It does not consulttypeMappings, so an instance installed only throughregisterType(Class, Object)-- which writes to that map and nowhere else -- is invisible to a name-based presence check, even thoughgetBean(Class)returns it. A caller asking "is it here?" rather than "what is it called?" needs both.Name-derived candidates are additionally type-checked against the retrieved singleton: the definition and supplier sources match on declared metadata, so a definition naming
typeplus a singleton of something else under the same name would otherwise report a presence that is not there.Neither branch instantiates:
getSingleton(String, boolean)is a Level 1 cache read with no early reference, andtypeMappingsholds already-constructed objects.- Parameters:
type- the type to look for- Returns:
trueif a constructed instance assignable totypeis already held- Since:
- 6.3.0
-
getBeanNamesForType
Get bean names for type.Enumerates names from
singletons,beanDefinitionsandsupplierTypes, plus any parent container. It deliberately answers "what is it called", not "is it here":typeMappingsholds instances bound byregisterType(Class, Object)under no name at all, so they cannot appear here. For a presence question, usehasConstructedInstanceOfType(Class).- Parameters:
type- bean type- Returns:
- bean names
-
getAutowireCapableBeanFactory
Get autowire capable bean factory.- Returns:
- autowire factory
-
close
public void close()Close the container.@PreDestroyruns once per singleton instance, not once per name: an object bound under several names (a@Bean(name = {primary, alias})product, or any otheraddSingleton(java.lang.String, java.lang.Object)alias) is released once (C50). -
containsBean
Check if container contains bean.- Parameters:
name- bean name- Returns:
- true if contains
-
registerBean
Register bean with constructor arguments.- Parameters:
type- bean typeconstructorArgs- constructor arguments
-
getBeanName
Generate bean name for class. -
declaresOwnBeanName
True ifftypeitself carries an explicit@Component(value = ...)or@Service(value = ...)name -- the same conditiongetBeanName(Class)checks before falling back to a decapitalized-simple-name guess.Used by
getBean(Class)'s by-name shortcut (CR-01) to tell a deliberate, specific request ("typedeclares this exact name as its own") apart from an accidental collision between an unrelated implementer's explicit bean name and an interface's synthesized default -- only the former is a genuine self-match that should skip the priority/ambiguity adjudication a few lines below.- Parameters:
type- the requested type to check- Returns:
- true if
typedeclares its own non-empty@Component/@Servicename
-
refresh
public void refresh()Refresh the container. -
getSingletonValues
Get all singleton bean instances registered in this container (not including parent), each distinct instance exactly once.Every per-instance consumer --
@Scheduledscheduling,@PlayerCacheregistration,@ModuleEventHandlerregistration and their release -- iterates this view, so it is deduplicated by reference identity: an object bound under several names (a@Bean(name = {primary, alias})product, or any otheraddSingleton(java.lang.String, java.lang.Object)alias) appears once, while two distinct objects appear twice even if they areequals()(C50). The names themselves are untouched: each alias still resolves throughgetBean(String)and is still reported bygetBeanNamesForType(Class).The returned collection is a snapshot taken at the time of the call, in the iteration order of the underlying name map.
- Returns:
- unmodifiable collection holding each distinct singleton instance once
-
distinctSingletonInstances
Each distinct singleton instance in this container once, by reference identity -- the sameIdentityHashMaptechniquegetOrderedBeansOfType(Class)uses, and for the same reason: a bean may overrideequals(), and only the same object reached through two names may collapse (C50).- Returns:
- a new list of distinct singleton instances
-
setDisplayName
Set display name.- Parameters:
displayName- display name
-
setClassLoader
Set class loader.- Parameters:
classLoader- class loader
-
registerShutdownHook
public void registerShutdownHook()Register shutdown hook. -
setParent
Set parent container.- Parameters:
parent- parent container
-
getBeanFactory
Get bean factory.- Returns:
- bean factory
-
getClassLoader
Get class loader.- Returns:
- class loader
-
markConfigurationAssembled
Records that the scan's assembly phase runs the@Beanmethods ofconfiguration(package-visible forComponentScanner).- Parameters:
configuration- a@Configurationinstance this container holds- Returns:
truethe first time for this instance,falseon every later call
-
registerBeanDefinition
Register bean definition.- Parameters:
name- bean namedefinition- bean definition
-
invalidateResolvedTypeCache
private void invalidateResolvedTypeCache()Invalidates the by-type assignability resolution cache (resolvedTypeCache).Called whenever a new bean definition or singleton is registered, so a subsequently registered implementation of an already-resolved type participates in the next adjudication instead of being masked by a resolution cached before it existed (D-12). Never touches
typeMappings-- that map holds author-declared bindings (registerType/registerTypeSupplier/registerSingleton's own concrete-class binding), which must survive registration of unrelated beans. -
createBean
Create bean from definition. Uses three-level cache to support circular dependency resolution for setter injection.- Parameters:
name- bean namedefinition- bean definition- Returns:
- created bean
-
createBeanWithConstructorInjection
Create bean using constructor auto-wiring. Resolves constructor parameters from the container.- Parameters:
beanClass- the class to instantiate- Returns:
- new bean instance
-
findMatchingConstructor
private Constructor<?> findMatchingConstructor(Class<?> beanClass, Object[] args) throws NoSuchMethodException Find a matching constructor for the given arguments. Supports interface/superclass parameter types and primitive type boxing.- Parameters:
beanClass- the class to find constructor forargs- constructor arguments- Returns:
- matching constructor
- Throws:
NoSuchMethodException
-
isAssignable
Check if a value can be assigned to a parameter type. Handles primitive types, interfaces, and inheritance.- Parameters:
paramType- the parameter typevalue- the value to check- Returns:
- true if assignable
-
isPrimitiveWrapperOf
Check if wrapper is the wrapper class for primitive type. -
invokePostConstructMethods
Invoke methods annotated with @PostConstruct.- Parameters:
bean- the bean instance
-
invokePreDestroyMethods
Invoke methods annotated with @PreDestroy on a bean.- Parameters:
bean- the bean instance
-
addBeanPostProcessor
Add bean post processor.- Parameters:
processor- bean post processor
-
setAopProxyResolver
Sets the AOP proxy resolver for this container.Must be called before
refresh(): the resolver participates in bean instantiation, not in post-processing, so beans created earlier would not be proxied.- Parameters:
resolver- the resolver, or null to disable AOP for this container
-
getAopProxyResolver
Gets the AOP proxy resolver for this container.- Returns:
- the resolver, or null if AOP is not wired
-
getBeanDefinitionNames
Get all bean names.- Returns:
- bean names
-
getBeansOfType
Get beans of type.- Parameters:
type- bean type- Returns:
- beans map
-
getOrderedBeansOfType
Get beans of type ordered by @Service priority. Higher priority beans come first.- Parameters:
type- bean type- Returns:
- ordered list of beans
-
getHighestPriorityBean
Get the highest priority bean of the given type.- Parameters:
type- bean type- Returns:
- highest priority bean or null
-
getServicePriority
Get service priority from @Service annotation.- Parameters:
clazz- the class to check- Returns:
- priority value (default 0)
-
isSingleton
Check if bean is singleton.- Parameters:
name- bean name- Returns:
- true if singleton
-
isPrototype
Check if bean is prototype.- Parameters:
name- bean name- Returns:
- true if prototype
-
getType
Get bean type.- Parameters:
name- bean name- Returns:
- bean type
-
preInstantiateSingletons
public void preInstantiateSingletons()Initialize all singletons. -
start
public void start()Start the container. -
stop
public void stop()Stop the container. -
isRunning
public boolean isRunning()Check if container is running.- Returns:
- true if running
-
scanComponents
Scan components in packages.- Parameters:
basePackages- packages to scan
-
processConfigurationClass
Process configuration class.Reads
@ComponentScanthroughMergedAnnotationResolver.find(java.lang.Class<?>, java.lang.Class<A>)rather than a bareconfigClass.getAnnotation(ComponentScan.class)-- a class meta-annotated with@UltiToolsModulecarries itsscanBasePackages()/scanBasePackageClasses()values onto the merged@ComponentScanview via their@AliasFordeclarations (D-01), which a bare lookup would miss entirely. The result is additive acrossvalue(),basePackages()and the packages named bybasePackageClasses()-- in that declaration order, with duplicates collapsed to their first occurrence (GEN-06) -- not a first-match choice among them.- Parameters:
configClass- configuration class
-