This commit updates the AOT infrastructure to handle generated resources
in a similar fashion than generated classes: naming conventions, feature
prefixes, and uniqueness are applied.
The new abstraction also provides a more explicit contract that guides
users to either create the resource or create it if it does not exist
and validate its content if it does.
As part of this change ClassNameGenerator has been renamed to
NameGenerator as it is responsible to generate names for both classes
and resources.
Closes gh-35862
Now that all `PropertyAccessorUtils` has been removed and replaced by
`PropertyPath` and local private methods, we can officially deprecate
this utility class and remove it in the future.
Closes gh-37275
Prior to this commit, `DataBinder` and the property accessor
hierarchy relied on `PropertyAccessorUtils` and several
independent scanners for property paths.
This commit migrates all of them to `PropertyPath`, so
there is exactly one parser deciding what a well-formed property
path is, used identically for policy checks and for actual
navigation.
This removes long standing protected methods like
A`getPropertyAccessorForPropertyPathi` and `getFinalPath` from
`bstractNestablePropertyAccessor`. The path is now parsed exactly
once per public entry point, via the new `resolvePropertyPath`, which
returns a `ResolvedProperty`. Then, property navigation walks
the parsed segment list rather than re-scanning partial strings.
Any subclass overriding the removed method will need to adapt.
This removal initially conflicted with gh-37252 (maxNestedPathDepth
support). The public configuration remains but the actual behavior
changed; it is replaced with `PropertyPath.Options` which enforces
limit right after parsing, before property navigation begins.
The exception thrown changes from `InvalidPropertyException` to
`InvalidPropertyPathException`.
This commmit also reverts the `map[']` / `map["]` quoting behavior
from gh-36765 as it is incompatible with the new grammar.
See gh-37275
Prior to this commit, bean property path support would be duplicated in
`AbstractNestablePropertyAccessor` and `PropertyAccessorUtils`. This
means they both supported the parsing, validation and extraction of path
segments. Implementations were not always in sync and could cause
issues.
This commit introduces a new `PropertyPath` type that holds the
canonical form of the property path and the parsed path segments for
property access. This implements an efficient parser that rejects
invalid property paths early if they don't match the new grammar.
`PropertyPath.parse(String, Options)` additionally accepts a maximum
nesting depth, rejecting an excessively deep path immediately after
parsing and before any navigation of an object graph begins.
This moves the implementation introduced in gh-37252, but keeps the
public configuration in place.
`InvalidPropertyPathException` is introduced to report a syntactically
invalid path, as distinct from a syntactically valid path that
happens not to resolve against a particular target object (see
`NotReadablePropertyException` and `NotWritablePropertyException`).
It extends `PropertyAccessException`, but not `InvalidPropertyException`.
Malformed paths should be collected into
`PropertyBatchUpdateException` alongside other per-property failures.
See gh-37275
AbstractNestablePropertyAccessor resolves a nested property path
recursively, one recursive call per path segment, and there was
previously no limit on the nesting depth. Consequently, a sufficiently
deeply nested property path -- for example, against a self-referential
type -- could exhaust the current thread's call stack, resulting in a
StackOverflowError which lacks useful diagnostics for developers
attempting to assess what went wrong.
Note that the existing autoGrowCollectionLimit bounds array and
collection growth, not path depth.
The same is true for constructor binding via DataBinder.construct(),
which constructs a nested constructor argument recursively through a
nested property path. Path segments are constrained to declared
constructor parameters there, but a self-referential type nonetheless
permits an arbitrarily deep path.
With this commit, each property accessor tracks the number of nested
properties traversed to reach the object that it wraps, and an
InvalidPropertyException is thrown once the configured (or default)
maxNestedPathDepth limit is exceeded, with a message that reports the
configured limit. The limit applies regardless of autoGrowNestedPaths,
since resolving an existing deep object graph recurses in the same
manner as auto-growing one. Tracking the depth per property accessor
rather than threading it through the recursion allows the recursion to
dispatch through the protected
getPropertyAccessorForPropertyPath(String) method, which subclasses may
override, and avoids deriving the depth from the nested path, which
would require rescanning an ever longer path prefix at each level.
Constructor binding likewise tracks the nesting depth while
constructing nested objects as well as indexed and mapped elements, and
throws the same InvalidPropertyException once the limit is exceeded.
The maxNestedPathDepth (which defaults to 100) can be configured on a
per-use-case basis via ConfigurablePropertyAccessor or DataBinder,
which applies it to constructor binding directly and supplies it to the
property accessor via its binding result. In contrast to the auto-grow
collection limit, which is unlimited on a plain accessor, the nesting
depth is bounded by default even for programmatic property access,
since a large array or collection can be perfectly legitimate whereas a
deeply nested property path effectively never is.
Specifying zero for the maxNestedPathDepth disables support for nested
property paths altogether while continuing to allow simple, indexed,
and mapped property access, which is a reasonable way to constrain data
binding for a target object that is not intended to be traversed (such
as a flat DTO). However, negative values for maxNestedPathDepth are
always rejected.
Closes gh-37252
Before this commit, the implementation uses `ResolvableType::isInstance` which doesn't take JDK proxy into account, it fails if `proxyTargetClass = false`:
```
Bean named 'userDao' is expected to be of type 'org.springframework.cache.config.ExpressionCachingIntegrationTests$BaseDao<org.springframework.cache.config.ExpressionCachingIntegrationTests$User>' but was actually of type 'org.springframework.cache.config.$Proxy53'
org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'userDao' is expected to be of type 'org.springframework.cache.config.ExpressionCachingIntegrationTests$BaseDao<org.springframework.cache.config.ExpressionCachingIntegrationTests$User>' but was actually of type 'org.springframework.cache.config.$Proxy53'
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:212)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1312)
at org.springframework.cache.config.ExpressionCachingIntegrationTests.expressionIsCacheBasedOnActualMethod(ExpressionCachingIntegrationTests.java:42)
```
See gh-34687
Closes gh-37047
Signed-off-by: Yanming Zhou <zhouyanming@gmail.com>
As a follow up to 1b56f58999, this commit introduces
hasUnbalancedBrackets() in PropertyAccessorUtils, which
AbstractNestablePropertyAccessor uses to reject property paths with
unbalanced '[' or ']' brackets by throwing a
NotReadablePropertyException with an informative message, thereby
improving diagnostics for users.
See gh-36999
Previously, canonicalPropertyName() located the end of a [key]
expression via a naive indexOf("]") search, while
AbstractNestablePropertyAccessor.getPropertyNameKeyEnd() -- used during
actual property resolution -- tracked bracket nesting depth. This meant
the two methods could disagree on the canonical form of a property
path whose map key itself contains bracket characters (e.g.,
map['key[0]']).
Similarly, getNestedPropertySeparatorIndex() tracked whether a dot
separator occurs inside a [key] expression using a simple boolean
toggle that flips on both '[' and ']', which produces an incorrect
result when a key contains an odd net count of inner bracket
characters.
To address those inconsistencies, this commit extracts the private
getPropertyNameKeyEnd() method from AbstractNestablePropertyAccessor to
a package-private static utility method in PropertyAccessorUtils, so
that canonicalPropertyName() can reuse the same depth-aware bracket
matching, and getNestedPropertySeparatorIndex() has been reworked to
track bracket nesting depth instead of toggling a boolean flag.
Closes gh-36999
Previously, PropertyEditorRegistrySupport.addStrippedPropertyPaths()
recursively enumerated every combination of stripped/retained [key]
segments in a property path, producing 2^n - 1 variants for a path
with n bracket pairs.
To address that, this commit limits the recursion at a depth of 8,
preserving existing behavior for realistic property paths while
bounding the work done for paths with an unusually large number of
bracket segments.
Closes gh-37020
Prior to this commit, the List branch in
AbstractNestablePropertyAccessor's getPropertyValue() method called
list.get(index) unconditionally after invoking
growCollectionIfNecessary(), which implicitly relied on the list
throwing an IndexOutOfBoundsException for out-of-range access. Such an
exception is caught downstream and wrapped as an
InvalidPropertyException; however, any List implementation whose get()
method allocates elements on demand rather than throwing an
IndexOutOfBoundsException could bypass that check.
This behavior was also inconsistent with the Collection/Iterable branch
in the same method, which already performs an explicit `index >=
collection.size()` bounds check before attempting element access.
To address that, this commit aligns the List branch with the
Collection/Iterable branch by adding an explicit `index < 0 || index >=
list.size()` check immediately after the auto-grow attempt. If the
index remains out of bounds after growCollectionIfNecessary() runs –
for example, because growth was capped by autoGrowCollectionLimit or
auto-growing was disabled – an InvalidPropertyException is now thrown
rather than delegating to list.get() which may or may not throw an
exception.
Closes gh-37036
Prior to this commit, PropertyDescriptorUtils.determineBasicProperties()
incorrectly recognized static `get` and `is` accessor methods as
JavaBean read methods, in contrast to the standard
java.beans.Introspector, which has always excluded static methods from
property discovery. This regression was introduced in Spring Framework
6.0 when determineBasicProperties() replaced the delegation to
java.beans.Introspector for the fast property-discovery path used by
SimpleBeanInfoFactory. As a result, an unrelated static method such as
a singleton accessor could be exposed as a bean property, and
reflective access to such a property (for example, via BeanWrapperImpl)
could lead to a StackOverflowError if the property's value recursively
exposed the same static accessor.
To address that, this commit adds Modifier.isStatic(...) checks to the
`get` and `is` branches in determineBasicProperties(), mirroring the
equivalent check already present in
CachedIntrospectionResults.isPlainAccessor(). Static `set` methods
continue to be supported as write methods, consistent with the existing
behavior in ExtendedBeanInfo.
See gh-37068
Closes gh-37081
Signed-off-by: Arnab Nandy <arnab_nandy7@yahoo.com>
This commit addresses warnings across the code base related to:
- internal and public deprecations in Spring Framework
- deprecated Locale constructors
- deprecated URL constructors
- deprecated Thread#getId method
Revise the BeanRegistrar Javadoc to document the two distinct usage
modes: @Configuration/@Import and programmatic GenericApplicationContext
setup.
Clarify that implementations are not Spring components (requiring a
no-arg constructor and no dependency injection), and detail the ordering
guarantees for each mode.
Add missing tests
Signed-off-by: Stéphane Nicoll <stephane.nicoll@broadcom.com>
The resolveDependency() utility method in ParameterResolutionDelegate
resolves a dependency using the name of the parameter as a fallback
qualifier. That suffices for most use cases; however, there are times
when a custom parameter name should be used instead.
For example, for our Bean Override support in the Spring TestContext
Framework, an annotation such as @MockitoBean("myBean") specifies an
explicit name that should be used instead of name of the annotated
parameter.
Furthermore, introducing support for custom parameter names will
greatly simplify the logic in SpringExtension that will be required to
implement #36096.
To address those issues, this commit introduces an overloaded variant
of resolveDependency() which accepts a custom parameter name.
Internally, a custom DependencyDescriptor has been implemented to
transparently support this use case.
See gh-36096
Closes gh-36534