| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
Enumerate beans by their @Indexed types even when not implemented (#12960) * Enumerate beans by their @Indexed types even when not implemented A bean annotated with @Indexed(Marker.class), directly, through a stereotype or by a TypeElementVisitor at compile time, already has the index emitted by the processor into BeanDefinitionReference.getIndexes(), but the runtime stopped reading it in #12066. As a result BeanContext.getBeanDefinitions(Marker.class) did not return beans that do not implement Marker. DefaultBeanDefinitionService now also indexes each producer under its getIndexes() types that are not exposed types (for compiled, runtime registered and disabled beans), and getDefinitionIfEnabled accepts a producer whose reference is indexed by exactly the requested type. Index-only matches are enumeration-only: DefaultBeanContext filters them out of the instance lookup paths (getBean, getBeansOfType, containsBean, findBeanDefinition and collection injection), so getBean(Marker.class) throws NoSuchBeanException instead of returning a non-Marker instance. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * Fix indexed marker bean resolution * Optimize indexed runtime bean registration * Do not treat a bean merely indexed by a listener type as a listener A factory that implements `BeanDestroyedEventListener` is indexed by it, and the beans it produces inherit that annotation metadata - so with enumeration by index they turn up in `getBeanDefinitions(listenerType)` without being listeners. Dispatching then failed with a ClassCastException (`org.graalvm.polyglot.Engine cannot be cast to java.util.EventListener` in `GraalPyEngineFactory`). Filter the listener lookup through `isInjectableCandidate`, the same test the injection path already applies. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * Answer a self-indexed stereotype qualifier from the compile-time index (#12993) Answering "every bean annotated @X" means `getBeanDefinitions(Qualifiers.byStereotype(X))`, which tests the annotation metadata of every bean definition reference in the application, once per caller. An annotation meta-annotated with `@Indexed` by its own type has the beans carrying it indexed at compile time, and that index is exhaustive for it, so the qualifier can be answered by looking the annotation type up in the index instead. `AnnotationStereotypeQualifier` reports that type through `FilteringQualifier.getIndexedType()` and `getBeanDefinitions(Qualifier)` takes it when present, so every caller benefits without changing a line, and an annotation that is not self-indexed keeps the scan. `@Endpoint`, `@ServerWebSocket`, `@FunctionBean` and `@MessageListener` are indexed by themselves. That covers `EndpointSensitivityProcessor` and `AbstractEndpointRouteBuilder`, `ServerWebSocketProcessor`, `DefaultLocalFunctionRegistry` and `MessagingApplication`. `@MessageListener` is the stereotype the transport specific annotations are built on, so indexing it indexes every listener transitively. On a context with 808 bean definitions the first lookup drops from 2.66ms to 0.17ms; warm, from 46.5us to 4.6us. `collectBeanCandidates` collected into a `HashSet`, so `getBeanDefinitions(Class)` answered in an identity-hash order that differed on every JVM run, while the scan it now replaces answers in bean definition reference order. `AbstractEndpointRouteBuilder` registers endpoint routes in that order, so the swap would have made route registration non-deterministic. Collecting into a `LinkedHashSet` keeps the reference order, makes the two agree exactly, and removes a pre-existing source of run-to-run variation in every typed lookup. Co-authored-by: Claude Opus 5 <noreply@anthropic.com> * Apply the review feedback from #12993 The comments landed after the merge, so they are addressed here. - `getBeanDefinitions(Qualifier)` casts the `Argument` rather than the returned `Collection`, so the unchecked conversion is contained and the return type stays generic. - `FunctionBeanIndexedLookupSpec` asserts that each definition names its function before unwrapping the `Optional`, so a missing value fails on its own assertion instead of throwing out of a spread chain. - `EndpointIndexedLookupSpec` asserts the index through `getBeanDefinitionReferences()` rather than casting a `BeanDefinition` to a `BeanDefinitionReference`. - Four unused `Qualifiers` imports and one unused `BeanDefinitionReference` import, left behind when the specs switched to comparing against a real full scan. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * Do not resolve a bean factory through the scope of the bean it produces Creating a bean from a factory method resolves the factory bean while the produced bean's segment is on the path. When the factory is a `@Prototype`, `findCustomScope` answered with the scope declared on the injection point, and for that segment the injection point is the produced bean, which carries the factory method's scope. The factory was therefore created through the very scope that was creating the produced bean, entering `CustomScope.getOrCreate` a second time while the first call had not returned. A scope backed by `ConcurrentHashMap.computeIfAbsent`, as `AbstractConcurrentCustomScope` is, answers that with `IllegalStateException: Recursive update` whenever the two bean keys share a bin. `BeanKey` hashes by `Argument.typeHashCode()`, which folds in the identity based `Class.hashCode()`, so whether they collide differs on every JVM run. That is what made `ScopedProxyCircularDependencySpec.lazy scoped proxy can discard stale injection path before resolving target` fail intermittently. A scope declared on a produced bean says nothing about the lifetime of the factory that produces it, so the injection point scope no longer applies when the bean being resolved is that factory. A scope declared on a genuine injection point, such as `@InjectScope` on a constructor parameter, is unaffected. `CustomScopeReentrancySpec` asserts the scope is entered once rather than re-entered, so it fails deterministically without depending on the bin collision. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * Cover the lookup of a stereotype that is not indexed `@Indexed` is only an optimisation hint, so an annotation without it has to keep being answered by filtering every bean definition reference. Nothing asserted that, since every existing spec uses a self-indexed annotation. `NonIndexedStereotypeLookupSpec` covers both ways the scan is still taken: a stereotype that is not indexed at all, and a self-indexed one resolved through `Qualifiers.byStereotype(String)`, which has no type to index by. In each case the qualifier reports no indexed type, the beans found match enumerating every bean definition, and for the non-indexed stereotype the index is empty, so the scan is what answered. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * Hold the indexed argument on the qualifier rather than building one per lookup `getBeanDefinitions(Qualifier)` built an `Argument` from the indexed type on every call, which was more than half the cost of an otherwise cached lookup. The qualifier knows the type when it is constructed, so it holds the `Argument` instead and `FilteringQualifier` reports that rather than a `Class`. Warm, on a context with 808 bean definitions, the lookup goes from 56ns to 38ns and stops allocating. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * Never resolve an index-only bean as the type it is merely indexed by A `@Factory` that is itself a `BeanDestroyedEventListener` carries `@Indexed(BeanDestroyedEventListener)`, and the beans it produces inherit that index. `getTypeToListenerMap` enumerated the listeners with `getBeanDefinitions` and instantiated whatever came back, so shutting a context down tried to resolve the produced bean as a listener: java.lang.ClassCastException: class org.graalvm.polyglot.Engine cannot be cast to class java.util.EventListener `GraalPyEngineFactory` is exactly that shape, which is how the Python module found it. Enumeration by an indexed type is not resolution by it, so the listener lookup keeps only the candidates that really implement the listener type. `findExecutableMethod` and `findProxyBeanDefinition` had the same hazard, taking the first definition and an `isProxy()` definition respectively without checking the bean is a candidate for the requested type, so they filter too. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com> | 19 小时前 | |
Add some missing readmes | 5 年前 | |
don’t cache failures until the context is running (#12397) * untrack buildSrc/gradle.properties * don’t cache failures until the context is running Fixed with the help of AI > Two changes in DefaultBeanDefinitionService.java BeanDefinitionProducer: > 1. getReferenceIfEnabled (line 659): Only permanently null out this.reference when context.isRunning(). Before startup, the reference is preserved so conditions can be re-evaluated later. > 2. getDefinitionIfEnabled (line 690): Only set this.definition = DEFINITION_DISABLED_SENTINEL when context.isRunning(). Same rationale. > Why this works: Before start(), the environment hasn't processed property sources into the resolver catalog, so property-based @Requires conditions fail incorrectly. By not caching these failures permanently, the conditions are re-evaluated after start() when properties are available. The performance impact is negligible — the checkEnabledBeans ForkJoinTask and normal bean lookups after startup will still permanently cache results as before. | 6 个月前 |
| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
| 19 小时前 | ||
| 5 年前 | ||
| 6 个月前 |