001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.juneau.commons.inject; 018 019import static org.apache.juneau.commons.reflect.ReflectionUtils.*; 020import static org.apache.juneau.commons.utils.CollectionUtils.*; 021import static org.apache.juneau.commons.utils.Shorts.*; 022import static org.apache.juneau.commons.utils.StringUtils.*; 023 024import java.util.*; 025import java.util.concurrent.*; 026import java.util.function.*; 027import org.apache.juneau.commons.collections.*; 028import org.apache.juneau.commons.logging.*; 029import org.apache.juneau.commons.reflect.*; 030import org.apache.juneau.commons.settings.*; 031 032/** 033 * Basic implementation of {@link WritableBeanStore}. 034 * 035 * <p> 036 * A simple storage database for beans keyed by type and name. 037 * Used to retrieve and instantiate beans using an injection-like API. 038 * It's similar in concept to the injection framework of Spring but greatly simplified in function and not intended to implement a full-fledged injection framework. 039 * 040 * <p> 041 * Beans can be stored with or without names. Named beans are typically resolved using 042 * the <ja>@Name</ja> or <ja>@Qualified</ja> annotations on constructor or method parameters. 043 * 044 * <p> 045 * This implementation is thread-safe and supports parent bean stores for hierarchical bean resolution. 046 * 047 * <h5 class='section'>Resolution order:</h5> 048 * <p> 049 * Bean lookups consult the following sources in this order, returning the first match: 050 * <ol> 051 * <li>{@linkplain #BasicBeanStore(BeanStore,BeanStore) Overriding parent} (e.g. a Spring application context bridge), if non-{@code null}. 052 * <li>Local entries added via {@link #addBean(Class,Object) addBean} / {@link #addSupplier(Class,Supplier) addSupplier}. 053 * <li>Regular {@linkplain #BasicBeanStore(BeanStore) parent}, if non-{@code null}. 054 * <li>Local default suppliers added via {@link #addDefaultSupplier(Class,Supplier) addDefaultSupplier} (memoizer-backed framework defaults). 055 * </ol> 056 * 057 * <h5 class='section'>See Also:</h5><ul> 058 * <li class='jc'>{@link BeanStore} - Read-only bean lookup interface 059 * <li class='jc'>{@link WritableBeanStore} - Writable bean store interface 060 * </ul> 061 */ 062@SuppressWarnings({ 063 "java:S135", // @PreDestroy/@Bean discovery loops use per-element continue guards for clarity; refactoring obscures the per-annotation filter chain 064 "java:S1192", // Duplicated literals (argument/property names) read more clearly inline than as constants 065 "java:S3011", // setAccessible(true) is required to invoke private/package-private @Bean members and @PreDestroy methods via reflection 066 "java:S3776", // matchesConditions and registerConfiguration intentionally centralize the @Conditional/@Bean discovery state machine; splitting hurts cohesion 067 "resource", // BeanStore is a fluent AutoCloseable; self-returns and owned/sentinel stores are not new resources to close. 068 "unchecked" // Bean maps are keyed by Class<T>, so (T)/(Supplier<T>)/(Class<? extends T>) casts on stored suppliers and bindings match the requested bean type 069}) 070public class BasicBeanStore implements WritableBeanStore { 071 private static final RichLogger LOGGER = RichLogger.getLogger(BasicBeanStore.class.getName()); 072 073 /** 074 * Static reusable empty instance. 075 * 076 * <p> 077 * Useful as a placeholder when an API requires a {@link BeanStore} but the caller has no beans to 078 * register — e.g. as the parent argument to a builder that only needs the bean store for 079 * downstream parameter resolution. 080 * 081 * <p> 082 * <b>Treat this instance as read-only.</b> It's typed as the concrete class so it can be passed 083 * to APIs that take {@link WritableBeanStore} or {@code BasicBeanStore}, but callers should not 084 * call any mutating methods on it — doing so would leak state between unrelated callers. 085 * Code that legitimately needs to add beans should construct its own {@code new BasicBeanStore()}. 086 */ 087 public static final BasicBeanStore INSTANCE = new BasicBeanStore(); 088 089 private final ConcurrentHashMap<Class<?>,ConcurrentHashMap<String,Supplier<?>>> entries; 090 private final BeanStore parent; 091 private final BeanStore overridingParent; 092 @SuppressWarnings({ 093 "java:S3077" // volatile is required here for correct double-checked-locking safe-publication of the lazily-created StackOverlay in pushOverlay(); the reference is publish-once and never compound-mutated. 094 }) 095 private volatile StackOverlay overlayStack; 096 @SuppressWarnings({ 097 "java:S3077" // volatile is required for safe publication of the lazily-created holder; the reference is publish-once and never compound-mutated. 098 }) 099 private volatile Extras extras; 100 @SuppressWarnings({ 101 "java:S3077" // volatile is required for safe publication of the lazily-created holder; the reference is publish-once and never compound-mutated. 102 }) 103 private volatile Tracking tracking; 104 private volatile boolean closed; 105 106 /** 107 * Rarely-used state, created on first write so a short-lived store that only holds a few entries (e.g. one per 108 * request) doesn't allocate it. 109 */ 110 private static final class Extras { 111 final ConcurrentHashMap<Class<?>,ConcurrentHashMap<String,Supplier<?>>> defaults = new ConcurrentHashMap<>(); 112 // Holds only non-default metadata (@Primary / @Order / priority); an absent entry means BeanSourceMeta.DEFAULT. 113 final ConcurrentHashMap<Class<?>,ConcurrentHashMap<String,BeanSourceMeta>> entryMetadata = new ConcurrentHashMap<>(); 114 final ConcurrentHashMap<Class<?>,Class<?>> typeBindings = new ConcurrentHashMap<>(); 115 final Set<Class<?>> registeredConfigurations = Collections.synchronizedSet(new HashSet<>()); 116 } 117 118 /** Beans handed out by this store, in resolution order, for {@link #close()} callbacks. Guarded by itself. */ 119 private static final class Tracking { 120 final List<Object> beans = new ArrayList<>(4); 121 final Set<Object> identities = Collections.newSetFromMap(new IdentityHashMap<>(8)); 122 } 123 124 private Extras extras() { 125 var x = extras; 126 if (x == null) { 127 synchronized (this) { 128 x = extras; 129 if (x == null) { 130 x = new Extras(); 131 extras = x; 132 } 133 } 134 } 135 return x; 136 } 137 138 private ConcurrentHashMap<String,Supplier<?>> defaultTypeMap(Class<?> beanType) { 139 var x = extras; 140 return x == null ? null : x.defaults.get(beanType); 141 } 142 143 private void putEntryMeta(Class<?> beanType, String key, BeanSourceMeta meta) { 144 if (meta == BeanSourceMeta.DEFAULT) { 145 // Default metadata is represented by absence; drop any non-default value this key previously had. 146 var x = extras; 147 if (x != null) { 148 var m = x.entryMetadata.get(beanType); 149 if (m != null) 150 m.remove(key); 151 } 152 } else { 153 extras().entryMetadata.computeIfAbsent(beanType, k -> new ConcurrentHashMap<>()).put(key, meta); 154 } 155 } 156 157 /** 158 * No-arg constructor. Equivalent to {@code new BasicBeanStore(null)}. 159 * 160 * <p> 161 * Creates a standalone, parent-less bean store. All lookups consult only locally-registered beans and 162 * default suppliers. 163 */ 164 public BasicBeanStore() { 165 this(null, null); 166 } 167 168 /** 169 * Constructor. 170 * 171 * @param parent The parent bean store. Can be <jk>null</jk>. Bean searches are performed recursively up this parent chain 172 * <i>after</i> local entries are checked. 173 */ 174 public BasicBeanStore(BeanStore parent) { 175 this(parent, null); 176 } 177 178 /** 179 * Constructor that accepts an overriding parent bean store. 180 * 181 * <p> 182 * The {@code overridingParent} is consulted <i>before</i> any local entries during bean lookup, allowing an outer 183 * scope (typically a Spring {@code ApplicationContext} bridge) to take precedence over local registrations. The 184 * regular {@code parent} continues to be consulted as a fallback after local entries. 185 * 186 * <p> 187 * Final resolution order: 188 * <ol> 189 * <li>{@code overridingParent} (if non-{@code null}) 190 * <li>local entries (added via {@link #addBean(Class,Object) addBean} / {@link #addSupplier(Class,Supplier) addSupplier}) 191 * <li>regular {@code parent} (if non-{@code null}) 192 * <li>local default suppliers (added via {@link #addDefaultSupplier(Class,Supplier) addDefaultSupplier}) 193 * </ol> 194 * 195 * @param parent The parent bean store, used as a fallback after local entries. Can be <jk>null</jk> (this store then has no parent fallback). 196 * @param overridingParent The overriding parent bean store, consulted before local entries. Can be <jk>null</jk> (no overriding parent is consulted). 197 */ 198 public BasicBeanStore(BeanStore parent, BeanStore overridingParent) { 199 this.parent = parent; 200 this.overridingParent = overridingParent; 201 entries = new ConcurrentHashMap<>(); 202 addSupplier(BasicBeanStore.class, ()->this, null); 203 addSupplier(BeanStore.class, ()->this, null); 204 addSupplier(WritableBeanStore.class, ()->this, null); 205 } 206 207 /** 208 * Same as {@link #addBean(Class,Object)} but returns the bean instead of this object for fluent calls. 209 * 210 * @param <T> The class to associate this bean with. 211 * @param beanType The class to associate this bean with. Must not be <jk>null</jk>. 212 * @param bean The bean. Can be <jk>null</jk> (stored as an explicit <jk>null</jk> binding, distinct from no binding at all). 213 * @return The bean. 214 */ 215 @Override 216 public <T> T add(Class<T> beanType, T bean) { 217 add(beanType, bean, null); 218 return bean; 219 } 220 221 /** 222 * Same as {@link #addBean(Class,Object,String)} but returns the bean instead of this object for fluent calls. 223 * 224 * @param <T> The class to associate this bean with. 225 * @param beanType The class to associate this bean with. Must not be <jk>null</jk>. 226 * @param bean The bean. Can be <jk>null</jk> (stored as an explicit <jk>null</jk> binding, distinct from no binding at all). 227 * @param name The bean name if this is a named bean. Can be <jk>null</jk> for unnamed beans. 228 * @return The bean. 229 */ 230 @Override 231 public <T> T add(Class<T> beanType, T bean, String name) { 232 addBean(beanType, bean, name); 233 return bean; 234 } 235 236 /** 237 * Adds an unnamed bean of the specified type to this store. 238 * 239 * @param <T> The class to associate this bean with. 240 * @param beanType The class to associate this bean with. Must not be <jk>null</jk>. 241 * @param bean The bean. Can be <jk>null</jk> (stored as an explicit <jk>null</jk> binding, distinct from no binding at all). 242 * @return This object. 243 */ 244 @Override 245 public <T> BasicBeanStore addBean(Class<T> beanType, T bean) { 246 return addBean(beanType, bean, null); 247 } 248 249 /** 250 * Adds a named bean of the specified type to this store. 251 * 252 * @param <T> The class to associate this bean with. 253 * @param beanType The class to associate this bean with. Must not be <jk>null</jk>. 254 * @param bean The bean. Can be <jk>null</jk> (stored as an explicit <jk>null</jk> binding, distinct from no binding at all). 255 * @param name The bean name if this is a named bean. Can be <jk>null</jk> for unnamed beans. 256 * @return This object. 257 */ 258 @Override 259 public <T> BasicBeanStore addBean(Class<T> beanType, T bean, String name) { 260 return addSupplier(beanType, () -> bean, name); 261 } 262 263 /** 264 * Adds a supplier for an unnamed bean of the specified type to this store. 265 * 266 * <p> 267 * The supplier will be invoked lazily when the bean is first requested. 268 * 269 * @param <T> The class to associate this bean with. 270 * @param beanType The class to associate this bean with. Must not be <jk>null</jk>. 271 * @param bean The bean supplier. Must not be <jk>null</jk>. 272 * @return This object. 273 */ 274 @Override 275 public <T> BasicBeanStore addSupplier(Class<T> beanType, Supplier<T> bean) { 276 return addSupplier(beanType, bean, null); 277 } 278 279 /** 280 * Adds a supplier for a named bean of the specified type to this store. 281 * 282 * <p> 283 * The supplier will be invoked lazily when the bean is first requested. 284 * 285 * @param <T> The class to associate this bean with. 286 * @param beanType The class to associate this bean with. Must not be <jk>null</jk>. 287 * @param bean The bean supplier. Must not be <jk>null</jk>. 288 * @param name The bean name if this is a named bean. Can be <jk>null</jk> for unnamed beans. 289 * @return This object. 290 */ 291 @Override 292 public <T> BasicBeanStore addSupplier(Class<T> beanType, Supplier<T> bean, String name) { 293 checkOpen(); 294 var typeMap = entries.computeIfAbsent(beanType, k -> new ConcurrentHashMap<>()); 295 var key = emptyIfNull(name); 296 typeMap.put(key, bean); 297 putEntryMeta(beanType, key, BeanSourceMeta.DEFAULT); 298 return this; 299 } 300 301 /** 302 * Adds a fallback supplier for an unnamed bean of the specified type to this store. 303 * 304 * <p> 305 * Default suppliers are consulted only after local {@linkplain #addBean(Class,Object) entries} and the regular 306 * {@linkplain #BasicBeanStore(BeanStore) parent} have been searched. They are intended for memoizer-backed 307 * framework defaults that should not shadow explicit user registrations or beans inherited from an 308 * {@linkplain #BasicBeanStore(BeanStore,BeanStore) overriding parent} (e.g. Spring). 309 * 310 * @param <T> The bean type. 311 * @param beanType The bean type. Must not be <jk>null</jk>. 312 * @param supplier The bean supplier. Must not be <jk>null</jk>. 313 * @return This object. 314 */ 315 @Override 316 public <T> BasicBeanStore addDefaultSupplier(Class<T> beanType, Supplier<T> supplier) { 317 return addDefaultSupplier(beanType, supplier, null); 318 } 319 320 /** 321 * Adds a fallback supplier for a named bean of the specified type to this store. 322 * 323 * <p> 324 * See {@link #addDefaultSupplier(Class,Supplier)} for ordering semantics. 325 * 326 * @param <T> The bean type. 327 * @param beanType The bean type. Must not be <jk>null</jk>. 328 * @param supplier The bean supplier. Must not be <jk>null</jk>. 329 * @param name The bean name. Can be <jk>null</jk> for unnamed beans. 330 * @return This object. 331 */ 332 @Override 333 public <T> BasicBeanStore addDefaultSupplier(Class<T> beanType, Supplier<T> supplier, String name) { 334 checkOpen(); 335 var typeMap = extras().defaults.computeIfAbsent(beanType, k -> new ConcurrentHashMap<>()); 336 var key = emptyIfNull(name); 337 typeMap.put(key, supplier); 338 return this; 339 } 340 341 /** 342 * Promotes every locally-registered default supplier to a local entry supplier of the same name and type. 343 * 344 * <p> 345 * Default suppliers normally resolve at tier 4 (after the regular parent walk), which means a parent 346 * store's default supplier wins over this store's default supplier when both are registered for the same 347 * type. Promoting moves each default into the tier-2 local-entry slot so that this store's framework 348 * defaults take precedence over the parent's same-type defaults — useful for embedded sub-contexts 349 * (e.g. per-mixin {@code RestContext}s) that need their own framework objects (serializer set, parser set, 350 * call logger, etc.) even though the parent is parent-linked for non-framework bean lookups. 351 * 352 * <p> 353 * The original default-supplier registrations are left in place; promoted entries that already exist as 354 * local entries are not overwritten. Promotion is idempotent and only affects suppliers registered prior 355 * to the call — later {@code addDefaultSupplier(...)} calls do not retroactively promote. 356 * 357 * @return This object. 358 * @since 10.0.0 359 */ 360 public BasicBeanStore promoteDefaultsToLocalSuppliers() { 361 checkOpen(); 362 var x = extras; 363 if (x != null) 364 x.defaults.forEach((beanType, typeMap) -> typeMap.forEach((name, supplier) -> 365 entries.computeIfAbsent(beanType, k -> new ConcurrentHashMap<>()).putIfAbsent(name, supplier))); 366 return this; 367 } 368 369 @Override 370 public Snapshot pushOverlay(BeanStore overlay) { 371 Objects.requireNonNull(overlay, "overlay must not be null"); 372 checkOpen(); 373 var stack = overlayStack; 374 if (stack == null) { 375 synchronized (this) { 376 stack = overlayStack; 377 if (stack == null) { 378 stack = new StackOverlay(); 379 overlayStack = stack; 380 } 381 } 382 } 383 stack.push(overlay); 384 return new Snapshot(this, overlay); 385 } 386 387 @Override 388 public void popOverlay(Snapshot snapshot) { 389 Objects.requireNonNull(snapshot, "snapshot must not be null"); 390 checkOpen(); 391 if (snapshot.owner() != this) 392 throw new IllegalStateException("Snapshot does not belong to this BeanStore (foreign-snapshot pop)."); 393 var stack = overlayStack; 394 if (stack == null || stack.depth() == 0) 395 throw new IllegalStateException("popOverlay invoked on an empty overlay stack."); 396 // LIFO discipline — the snapshot must identify the current top-of-stack frame. 397 var top = stack.peek(); 398 if (top != snapshot.frame()) 399 throw new IllegalStateException( 400 "Out-of-order popOverlay: supplied snapshot does not identify the top-of-stack overlay frame."); 401 stack.pop(); 402 } 403 404 /** 405 * Removes all beans from this store. 406 * 407 * <p> 408 * This operation only affects this store and does not affect the parent bean store. 409 * 410 * @return This object. 411 */ 412 @Override 413 public BasicBeanStore clear() { 414 checkOpen(); 415 entries.clear(); 416 var x = extras; 417 if (x != null) { 418 x.defaults.clear(); 419 x.entryMetadata.clear(); 420 x.registeredConfigurations.clear(); 421 } 422 return this; 423 } 424 425 /** 426 * Returns the unnamed bean of the specified type. 427 * 428 * <p> 429 * If no unnamed bean is found in this store, searches the parent store recursively. 430 * 431 * @param <T> The type of bean to return. 432 * @param beanType The type of bean to return. Must not be <jk>null</jk>. 433 * @return The bean, or {@link Optional#empty()} if not found. 434 */ 435 @Override 436 public <T> Optional<T> getBean(Class<T> beanType) { 437 return getBean(beanType, null); 438 } 439 440 441 /** 442 * Returns the named bean of the specified type. 443 * 444 * <p> 445 * If no bean with the specified name is found in this store, searches the parent store recursively. 446 * 447 * @param <T> The type of bean to return. 448 * @param beanType The type of bean to return. Must not be <jk>null</jk>. 449 * @param name The bean name. Can be <jk>null</jk> for unnamed beans. 450 * @return The bean, or {@link Optional#empty()} if not found. 451 */ 452 @Override 453 public <T> Optional<T> getBean(Class<T> beanType, String name) { 454 checkOpen(); 455 if (name == null) { 456 var unnamed = resolve(beanType, null).map(Supplier::get); 457 if (unnamed.isPresent()) 458 return unnamed.map(this::trackResolved); 459 return selectPrimary(beanType).map(this::trackResolved); 460 } 461 return resolve(beanType, name).map(Supplier::get).map(this::trackResolved); 462 } 463 464 /** 465 * Returns all beans of the specified type, keyed by bean name. 466 * 467 * <p> 468 * The map keys are the bean names (empty string for unnamed beans), and the values are the bean instances. 469 * Results from the parent store are included first, then beans from this store (which override parent beans with the same name). 470 * 471 * @param <T> The bean type. 472 * @param beanType The bean type. Must not be <jk>null</jk>. 473 * @return A map of bean names to bean instances. Never <jk>null</jk>. 474 */ 475 @Override 476 public <T> Map<String,T> getBeansOfType(Class<T> beanType) { 477 checkOpen(); 478 // Build the result respecting the priority order used by getBean / resolve: 479 // defaults (lowest) < parent < entries (local) < overridingParent < overlayStack (highest) 480 // Higher-priority maps overwrite lower-priority ones with the same name. 481 Map<String,T> result = map(); 482 var defaultMap = defaultTypeMap(beanType); 483 if (nn(defaultMap)) 484 defaultMap.forEach((name, supplier) -> result.put(name, (T)supplier.get())); 485 if (nn(parent)) 486 parent.getBeansOfType(beanType).forEach(result::put); 487 var typeMap = entries.get(beanType); 488 if (nn(typeMap)) 489 typeMap.forEach((name, supplier) -> result.put(name, (T)supplier.get())); 490 if (nn(overridingParent)) 491 overridingParent.getBeansOfType(beanType).forEach(result::put); 492 var stack = overlayStack; 493 if (nn(stack) && stack.depth() > 0) 494 stack.getBeansOfType(beanType).forEach(result::put); 495 if (result.isEmpty()) 496 return result; 497 return result.entrySet().stream() 498 .sorted(Comparator.comparingInt(e -> beanOrder(beanType, e.getKey()))) 499 .collect(LinkedHashMap::new, (m, e) -> m.put(e.getKey(), trackResolved(e.getValue())), Map::putAll); 500 } 501 502 /** 503 * Returns <jk>true</jk> if this store has a default supplier registered locally for the specified unnamed bean type. 504 * 505 * <p> 506 * Parent and overriding-parent stores are <i>not</i> consulted. 507 * 508 * @param beanType The bean type to check. Must not be <jk>null</jk>. 509 * @return <jk>true</jk> if a default supplier for the unnamed bean type is registered on this store. 510 */ 511 @Override 512 public boolean hasDefaultSupplier(Class<?> beanType) { 513 return hasDefaultSupplier(beanType, null); 514 } 515 516 /** 517 * Returns <jk>true</jk> if this store has a default supplier registered locally for the specified bean type and name. 518 * 519 * <p> 520 * Parent and overriding-parent stores are <i>not</i> consulted. 521 * 522 * @param beanType The bean type to check. Must not be <jk>null</jk>. 523 * @param name The bean name. Can be <jk>null</jk> for unnamed beans. 524 * @return <jk>true</jk> if a default supplier for the bean type and name is registered on this store. 525 */ 526 @Override 527 public boolean hasDefaultSupplier(Class<?> beanType, String name) { 528 var typeMap = defaultTypeMap(beanType); 529 return nn(typeMap) && typeMap.containsKey(emptyIfNull(name)); 530 } 531 532 /** 533 * Returns the default supplier registered locally for the specified unnamed bean type, or empty if none. 534 * 535 * <p> 536 * Parent and overriding-parent stores are <i>not</i> consulted. This returns the supplier itself, 537 * unwrapped from any resolution chain — it lets callers promote a memoizer-backed default 538 * supplier into a higher-precedence layer (e.g. a local entry) without re-invoking the underlying 539 * factory. 540 * 541 * @param <T> The bean type. 542 * @param beanType The bean type to look up. Must not be <jk>null</jk>. 543 * @return The locally-registered default supplier, or {@link Optional#empty()} if not present. 544 */ 545 public <T> Optional<Supplier<T>> getDefaultSupplier(Class<T> beanType) { 546 return getDefaultSupplier(beanType, null); 547 } 548 549 /** 550 * Returns the default supplier registered locally for the specified bean type and name, or empty if none. 551 * 552 * <p> 553 * Parent and overriding-parent stores are <i>not</i> consulted. This returns the supplier itself, 554 * unwrapped from any resolution chain — it lets callers promote a memoizer-backed default 555 * supplier into a higher-precedence layer (e.g. a local entry) without re-invoking the underlying 556 * factory. 557 * 558 * @param <T> The bean type. 559 * @param beanType The bean type to look up. Must not be <jk>null</jk>. 560 * @param name The bean name. Can be <jk>null</jk> for unnamed beans. 561 * @return The locally-registered default supplier, or {@link Optional#empty()} if not present. 562 */ 563 public <T> Optional<Supplier<T>> getDefaultSupplier(Class<T> beanType, String name) { 564 var typeMap = defaultTypeMap(beanType); 565 if (typeMap == null) 566 return oe(); 567 var supplier = typeMap.get(emptyIfNull(name)); 568 return supplier == null ? oe() : o((Supplier<T>) supplier); 569 } 570 571 /** 572 * Returns <jk>true</jk> if this store contains at least one unnamed bean of the specified type. 573 * 574 * <p> 575 * If not found in this store, searches the parent store recursively. 576 * 577 * @param beanType The bean type to check. Must not be <jk>null</jk>. 578 * @return <jk>true</jk> if this store contains at least one unnamed bean of the specified type. 579 */ 580 @Override 581 public boolean hasBean(Class<?> beanType) { 582 return hasBean(beanType, null); 583 } 584 585 /** 586 * Returns <jk>true</jk> if this store contains a bean of the specified type and name. 587 * 588 * <p> 589 * If not found in this store, searches the parent store recursively. 590 * 591 * @param beanType The bean type to check. Must not be <jk>null</jk>. 592 * @param name The bean name. Can be <jk>null</jk> for unnamed beans. 593 * @return <jk>true</jk> if this store contains a bean of the specified type and name. 594 */ 595 @Override 596 public boolean hasBean(Class<?> beanType, String name) { 597 return resolve(beanType, name).isPresent(); 598 } 599 600 @Override 601 public <T> WritableBeanStore addBeanType(Class<T> beanType, Class<? extends T> implType) { 602 checkOpen(); 603 extras().typeBindings.put(beanType, implType); 604 return this; 605 } 606 607 @Override 608 public <T> Optional<Class<? extends T>> getBeanType(Class<T> beanType) { 609 var x = extras; 610 var v = x == null ? null : (Class<? extends T>) x.typeBindings.get(beanType); 611 if (nn(v)) 612 return o(v); 613 if (nn(parent)) 614 return parent.getBeanType(beanType); 615 return oe(); 616 } 617 618 /** 619 * Returns the supplier for an unnamed bean of the specified type. 620 * 621 * <p> 622 * If no supplier is found in this store, searches the parent store recursively. 623 * 624 * @param <T> The bean type. 625 * @param beanType The bean type. Must not be <jk>null</jk>. 626 * @return The supplier, or {@link Optional#empty()} if no supplier of the specified type exists. 627 */ 628 @Override 629 public <T> Optional<Supplier<T>> getBeanSupplier(Class<T> beanType) { 630 return getBeanSupplier(beanType, null); 631 } 632 633 /** 634 * Resolves a supplier for a bean of the specified type and name. 635 * 636 * <p> 637 * If no supplier with the specified name is found in this store, searches the parent store recursively. 638 * 639 * @param <T> The bean type. 640 * @param beanType The bean type. Must not be <jk>null</jk>. 641 * @param name The bean name. Can be <jk>null</jk> for unnamed beans. 642 * @return The supplier, or {@link Optional#empty()} if no supplier of the specified type and name exists. 643 */ 644 protected <T> Optional<Supplier<T>> resolve(Class<T> beanType, String name) { 645 // (0) Push/pop overlay stack — wins over the construction-time overridingParent slot because pushed 646 // frames are "more recent". Lazily initialized; null until the first pushOverlay() call. 647 var stack = overlayStack; 648 if (nn(stack) && stack.depth() > 0) { 649 var fromStack = stack.getBeanSupplier(beanType, name); 650 if (fromStack.isPresent()) 651 return fromStack; 652 } 653 // (1) Overriding parent (e.g. Spring) — wins over local entries. 654 if (nn(overridingParent)) { 655 var fromOverriding = overridingParent.getBeanSupplier(beanType, name); 656 if (fromOverriding.isPresent()) 657 return fromOverriding; 658 } 659 // (2) Local regular entries. 660 var typeMap = entries.get(beanType); 661 if (nn(typeMap)) { 662 var key = emptyIfNull(name); 663 var supplier = typeMap.get(key); 664 if (nn(supplier)) 665 return o((Supplier<T>)supplier); 666 } 667 // (3) Regular parent fallback. 668 if (nn(parent)) { 669 var fromParent = parent.getBeanSupplier(beanType, name); 670 if (fromParent.isPresent()) 671 return fromParent; 672 } 673 // (4) Local default suppliers (memoizer-backed framework defaults). 674 var defaultMap = defaultTypeMap(beanType); 675 if (nn(defaultMap)) { 676 var key = emptyIfNull(name); 677 var supplier = defaultMap.get(key); 678 if (nn(supplier)) 679 return o((Supplier<T>)supplier); 680 } 681 return oe(); 682 } 683 684 /** 685 * Returns the supplier for a named bean of the specified type. 686 * 687 * <p> 688 * If no supplier with the specified name is found in this store, searches the parent store recursively. 689 * 690 * @param <T> The bean type. 691 * @param beanType The bean type. Must not be <jk>null</jk>. 692 * @param name The bean name. Can be <jk>null</jk> for unnamed beans. 693 * @return The supplier, or {@link Optional#empty()} if no supplier of the specified type and name exists. 694 */ 695 @Override 696 public <T> Optional<Supplier<T>> getBeanSupplier(Class<T> beanType, String name) { 697 checkOpen(); 698 return resolve(beanType, name); 699 } 700 701 @Override 702 public WritableBeanStore registerConfiguration(Class<?> configType) { 703 checkOpen(); 704 if (configType == null) 705 return this; 706 registerConfiguration(configType, extras().registeredConfigurations); 707 return this; 708 } 709 710 /** 711 * Finds and invokes a factory method that produces a bean of type <c>beanType</c>. 712 * 713 * <p> 714 * If <c>onClassOrObject</c> is a {@link Class}, only static methods are eligible. 715 * Otherwise, both instance and static methods on the object's class are eligible. 716 * A <jk>null</jk> <c>filter</c> accepts any qualifying method. 717 * 718 * <p> 719 * The search has two passes. The first pass considers public methods only (unchanged behavior). If it finds 720 * nothing, the second pass considers <em>non-public</em> methods across the class hierarchy, child class first, 721 * but only those carrying {@link Bean @Bean} (declared directly or inherited from a method they override; see 722 * {@link BeanAnnotation#find(MethodInfo)}). Bridge/synthetic methods, interface static methods and deprecated 723 * methods are skipped, as are parent methods overridden by an already-seen child method. A non-public method 724 * without {@link Bean @Bean} is never invoked, even when <c>filter</c> is <jk>null</jk>. 725 * Non-public methods are made accessible before invocation. 726 * 727 * <h5 class='section'>Example:</h5> 728 * <p class='bjava'> 729 * <jk>public class</jk> MyResource { 730 * <ja>@Bean</ja> 731 * RichLogger logger() { <jk>return new</jk> RichLogger(); } <jc>// Package-private, found by the second pass.</jc> 732 * } 733 * 734 * Optional<RichLogger> <jv>logger</jv> = <jv>beanStore</jv>.createBeanFromMethod(RichLogger.<jk>class</jk>, <jk>new</jk> MyResource()); 735 * </p> 736 * 737 * @param <T> The bean type. 738 * @param beanType The type of bean to create. Must not be <jk>null</jk>. 739 * @param onClassOrObject The object instance or {@link Class} whose methods are searched. 740 * Must not be <jk>null</jk>. 741 * @param filter Optional predicate restricting which methods are eligible. Can be <jk>null</jk> (any method qualifies). 742 * @param extraBeans Optional bean instances visible to parameter resolution for this call only. 743 * @return The created bean wrapped in an {@link Optional}, or {@link Optional#empty()} if no matching 744 * factory method was found. 745 * @throws BeanCreationException If a matching method was found but threw an exception during invocation. 746 */ 747 @Override 748 public <T> Optional<T> createBeanFromMethod(Class<T> beanType, Object onClassOrObject, Predicate<MethodInfo> filter, Object... extraBeans) { 749 reqnn("beanType", beanType); 750 reqnn("onClassOrObject", onClassOrObject); 751 Object resource = onClassOrObject instanceof Class ? null : onClassOrObject; 752 Class<?> resourceClass = onClassOrObject instanceof Class<?> onClassOrObject2 ? onClassOrObject2 : onClassOrObject.getClass(); 753 var first = info(resourceClass) 754 .getPublicMethod(m -> 755 m.isNotDeprecated() 756 && m.hasReturnType(beanType) 757 && (filter == null || filter.test(m)) 758 && (m.isStatic() || nn(resource)) 759 && m.canResolveAllParameters(this, extraBeans)) 760 .map(m -> { 761 try { 762 return m.<T>inject(this, resource, extraBeans); 763 } catch (Exception e) { 764 throw new BeanCreationException("Failed to create bean of type [" + beanType.getSimpleName() + "] via method [" + m.getName() + "]", e); 765 } 766 }); 767 if (first.isPresent()) 768 return first; 769 return createBeanFromNonPublicMethod(beanType, resourceClass, resource, filter, extraBeans); 770 } 771 772 /** 773 * Second pass of {@link #createBeanFromMethod}: non-public {@link Bean @Bean}-annotated methods, child class first. 774 */ 775 private <T> Optional<T> createBeanFromNonPublicMethod(Class<T> beanType, Class<?> resourceClass, Object resource, Predicate<MethodInfo> filter, Object[] extraBeans) { 776 var seen = new HashSet<String>(); // Signatures of non-private, non-static methods already visited (child-first). 777 for (var m : info(resourceClass).getAllMethods()) { 778 if (m.isBridge() || m.isSynthetic()) 779 continue; 780 if (m.isNotPrivate() && m.isNotStatic() && ! seen.add(m.getSignature())) 781 continue; 782 if (m.isPublic() || (m.isStatic() && m.getDeclaringClass().isInterface())) 783 continue; 784 if (BeanAnnotation.find(m).isEmpty()) 785 continue; 786 if (m.isNotDeprecated() 787 && m.hasReturnType(beanType) 788 && (filter == null || filter.test(m)) 789 && (m.isStatic() || nn(resource)) 790 && m.canResolveAllParameters(this, extraBeans)) 791 return Optional.ofNullable(BeanMethodInvoker.invoke(this, beanType, m, m.isStatic() ? null : resource, extraBeans)); 792 } 793 return Optional.empty(); 794 } 795 796 @Override /* Overridden from Object */ 797 public String toString() { 798 return r(properties()); 799 } 800 801 protected FluentMap<String,Object> properties() { 802 // @formatter:off 803 var entryList = list(); 804 entries.forEach((type, typeMap) -> typeMap.forEach((name, supplier) -> entryList.add(filteredBeanPropertyMap() 805 .a("type", cns(type)) 806 .a("bean", id(supplier.get())) 807 .a("name", name)))); 808 var defaultList = list(); 809 var xd = extras; 810 if (xd != null) 811 xd.defaults.forEach((type, typeMap) -> typeMap.forEach((name, supplier) -> defaultList.add(filteredBeanPropertyMap() 812 .a("type", cns(type)) 813 .a("bean", id(supplier.get())) 814 .a("name", name)))); 815 Object overridingParentValue = null; 816 if (nn(overridingParent)) { 817 if (overridingParent instanceof BasicBeanStore overridingParent2) 818 overridingParentValue = overridingParent2.properties(); 819 else 820 overridingParentValue = s(overridingParent); 821 } 822 var stack = overlayStack; 823 Object overlayStackValue = (nn(stack) && stack.depth() > 0) ? s(stack) : null; 824 return filteredBeanPropertyMap() 825 .a("entries", entryList) 826 .a("defaults", defaultList.isEmpty() ? null : defaultList) 827 .a("overlayStack", overlayStackValue) 828 .a("overridingParent", overridingParentValue) 829 .a("identity", id(this)) 830 .a("parent", parent instanceof BasicBeanStore parent2 ? parent2.properties() : s(parent)); 831 // @formatter:on 832 } 833 834 @Override 835 public void close() throws BeanCreationException { 836 if (closed) 837 return; 838 closed = true; 839 var errors = new BeanCreationException("Errors while invoking @PreDestroy callbacks."); 840 boolean hasErrors = false; 841 var t = tracking; 842 List<Object> beans = new ArrayList<>(); 843 if (t != null) 844 synchronized (t) { 845 beans.addAll(t.beans); 846 } 847 Collections.reverse(beans); 848 for (var bean : beans) { 849 for (var method : ClassInfo.of(bean).getAllMethods()) { 850 if (!method.getReturnType().is(void.class) || method.getParameterCount() != 0 || method.isAbstract() || method.isStatic()) 851 continue; 852 if (method.getAnnotations().stream().noneMatch(JsrSupport::isPreDestroyAnnotation)) 853 continue; 854 try { 855 method.invoke(bean); 856 } catch (Exception e) { 857 hasErrors = true; 858 errors.addSuppressed(e); 859 } 860 } 861 } 862 if (hasErrors) 863 throw errors; 864 } 865 866 private void registerConfiguration(Class<?> configType, Set<Class<?>> visited) { 867 if (!visited.add(configType)) 868 return; 869 var cfg = configType.getAnnotation(Configuration.class); 870 if (cfg == null) 871 throw new BeanCreationException("Type is not @Configuration: " + configType.getName()); 872 if (!matchesConditions(configType)) 873 return; 874 // Annotation arrays cannot contain null elements at runtime — Java rejects null defaults at 875 // declaration time and the compiler refuses null literals in annotation values. No defensive 876 // null check is needed here. 877 for (var imported : cfg.imports()) 878 registerConfiguration(imported, visited); 879 880 // Walk the type and its superclasses; collect @Bean members on each level. This honours 881 // the plan's "superclass inheritance of @Bean members" requirement. Order matters here: 882 // process the highest ancestor first so subclass @Bean methods can depend on superclass 883 // beans (analogous to Spring's @Configuration inheritance). 884 Object instance = null; 885 var hierarchy = new ArrayList<Class<?>>(); 886 for (var c = configType; c != null && c != Object.class; c = c.getSuperclass()) 887 hierarchy.add(c); 888 Collections.reverse(hierarchy); 889 for (var c : hierarchy) { 890 for (var field : c.getDeclaredFields()) { 891 var beanAnn = field.getAnnotation(Bean.class); 892 if (beanAnn == null) 893 continue; 894 if (!matchesConditions(field)) 895 continue; 896 try { 897 if (!java.lang.reflect.Modifier.isStatic(field.getModifiers()) && instance == null) 898 instance = BeanInstantiator.of(configType, this).run(); 899 field.setAccessible(true); 900 var value = field.get(java.lang.reflect.Modifier.isStatic(field.getModifiers()) ? null : instance); 901 addBeanWithMeta((Class<Object>)field.getType(), value, emptyIfNull(beanName(beanAnn)), BeanSourceMeta.from(field, beanAnn)); 902 } catch (Exception e) { 903 throw new BeanCreationException("Failed to register @Bean field: " + field, e); 904 } 905 } 906 for (var method : c.getDeclaredMethods()) { 907 var beanAnn = method.getAnnotation(Bean.class); 908 if (beanAnn == null) 909 continue; 910 if (!matchesConditions(method)) 911 continue; 912 try { 913 if (!java.lang.reflect.Modifier.isStatic(method.getModifiers()) && instance == null) 914 instance = BeanInstantiator.of(configType, this).run(); 915 method.setAccessible(true); 916 var value = MethodInfo.of(method).inject(this, java.lang.reflect.Modifier.isStatic(method.getModifiers()) ? null : instance); 917 addBeanWithMeta((Class<Object>)method.getReturnType(), value, emptyIfNull(beanName(beanAnn)), BeanSourceMeta.from(method, beanAnn)); 918 } catch (Exception e) { 919 throw new BeanCreationException("Failed to register @Bean method: " + method, e); 920 } 921 } 922 } 923 } 924 925 private boolean matchesConditions(java.lang.reflect.AnnotatedElement element) { 926 var classLoader = element instanceof Class<?> element2 ? element2.getClassLoader() : element.getClass().getClassLoader(); 927 var ctx = new ConditionContext(this, Settings.get(), classLoader, element); 928 for (var c : element.getAnnotationsByType(Conditional.class)) { 929 if (!BeanInstantiator.of(c.value(), this).run().matches(ctx)) { 930 LOGGER.fine(() -> "Skipping conditional element due to @Conditional: " + element); 931 return false; 932 } 933 } 934 for (var c : element.getAnnotationsByType(ConditionalOnClass.class)) { 935 try { 936 Class.forName(c.value(), false, classLoader); 937 } catch (@SuppressWarnings("unused") ClassNotFoundException e) { 938 LOGGER.fine(() -> "Skipping conditional element due to missing class " + c.value() + ": " + element); 939 return false; 940 } 941 } 942 for (var c : element.getAnnotationsByType(ConditionalOnMissingBean.class)) { 943 var type = c.value(); 944 var name = c.name().isEmpty() ? null : c.name(); 945 if (type == Object.class) { 946 if (name != null && anyLocalBeanNamed(name)) 947 return false; 948 } else if (hasBean(type, name)) { 949 return false; 950 } 951 } 952 for (var c : element.getAnnotationsByType(ConditionalOnProperty.class)) { 953 var value = Settings.get().get(c.name()).orElse(null); 954 if (value == null && !c.matchIfMissing()) 955 return false; 956 if (value != null && !c.havingValue().isEmpty() && !c.havingValue().equals(value)) 957 return false; 958 } 959 return true; 960 } 961 962 private boolean anyLocalBeanNamed(String name) { 963 var key = emptyIfNull(name); 964 var x = extras; 965 return entries.values().stream().anyMatch(m -> m.containsKey(key)) 966 || (x != null && x.defaults.values().stream().anyMatch(m -> m.containsKey(key))); 967 } 968 969 private String beanName(Bean bean) { 970 return bean.name().isEmpty() ? bean.value() : bean.name(); 971 } 972 973 private <T> void addBeanWithMeta(Class<T> beanType, T bean, String name, BeanSourceMeta meta) { 974 var key = emptyIfNull(name); 975 // Duplicate-check against LOCAL entries only. Beans present via the overridingParent or parent 976 // chain are allowed to coexist with a local registration here — the resolution order 977 // (overridingParent > local > parent > defaults) ensures the right entry wins at lookup time. 978 // This is what lets a test-time overlay installed in the overridingParent slot coexist with a 979 // local @Bean factory of the same (type, name) declared on a @Configuration class without 980 // triggering a spurious duplicate-bean error. 981 var typeMap = entries.computeIfAbsent(beanType, k -> new ConcurrentHashMap<>()); 982 if (typeMap.containsKey(key)) 983 throw new BeanCreationException("Duplicate bean: type=" + beanType.getName() + ", name=" + key); 984 typeMap.put(key, () -> bean); 985 putEntryMeta(beanType, key, meta); 986 } 987 988 private <T> T trackResolved(T bean) { 989 if (bean == null) 990 return null; 991 var t = tracking; 992 if (t == null) { 993 synchronized (this) { 994 t = tracking; 995 if (t == null) { 996 t = new Tracking(); 997 tracking = t; 998 } 999 } 1000 } 1001 synchronized (t) { 1002 if (t.identities.add(bean)) 1003 t.beans.add(bean); 1004 } 1005 return bean; 1006 } 1007 1008 private <T> Optional<T> selectPrimary(Class<T> beanType) { 1009 // @Primary is a local-entry-only signal. Default suppliers added via addDefaultSupplier() 1010 // always carry BeanSourceMeta.DEFAULT (primary = false), so default suppliers never 1011 // contributes a primary candidate — only entryMetadata is consulted here. Parent and 1012 // overridingParent stores (including Spring-backed delegates) are similarly excluded because 1013 // @Primary metadata only exists on beans this store registered itself. 1014 Set<String> primaryNames = st(); 1015 var x = extras; 1016 var em = x == null ? null : x.entryMetadata.get(beanType); 1017 if (nn(em)) 1018 em.forEach((name, meta) -> { if (meta.primary()) primaryNames.add(name); }); 1019 if (primaryNames.isEmpty()) 1020 return oe(); 1021 if (primaryNames.size() > 1) 1022 throw new BeanCreationException("Multiple @Primary candidates of type " + beanType.getName()); 1023 var primaryName = primaryNames.iterator().next(); 1024 return resolve(beanType, primaryName).map(Supplier::get); 1025 } 1026 1027 private int beanOrder(Class<?> type, String name) { 1028 // @Order / priority is sourced from registered entries only. Default suppliers always carry 1029 // BeanSourceMeta.DEFAULT (which already returns Integer.MAX_VALUE/2), so we fall through to 1030 // the same constant rather than tracking default-supplier metadata. 1031 var key = emptyIfNull(name); 1032 var x = extras; 1033 var m = x == null ? null : x.entryMetadata.get(type); 1034 if (m != null) 1035 return m.getOrDefault(key, BeanSourceMeta.DEFAULT).orderValue(); 1036 return Integer.MAX_VALUE / 2; 1037 } 1038 1039 private void checkOpen() { 1040 if (closed) 1041 throw new IllegalStateException("BeanStore has been closed."); 1042 } 1043 1044 private record BeanSourceMeta(boolean primary, Integer order, int priority) { 1045 static final BeanSourceMeta DEFAULT = new BeanSourceMeta(false, null, Integer.MAX_VALUE / 2); 1046 1047 int orderValue() { 1048 return order == null ? priority : order.intValue(); 1049 } 1050 1051 static BeanSourceMeta from(java.lang.reflect.AnnotatedElement element, Bean bean) { 1052 var order = element.getAnnotation(Order.class); 1053 return new BeanSourceMeta(element.getAnnotation(Primary.class) != null, order == null ? null : order.value(), bean.priority()); 1054 } 1055 } 1056}