Class SimpleContainer
-
Nested Class Summary
Nested Classes -
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 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).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.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
-
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
-
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. -
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).- Returns:
- unmodifiable collection of singleton beans
-
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
-
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
-