Document StandardTypeLocator configuration to support user types

Prior to this commit, it was unclear to users and third parties that it
is necessary to manually configure a StandardTypeLocator with a
specific ClassLoader to ensure that the SpEL expression parser is able
to reliably locate user types.

For example, the StandardBeanExpressionResolver in the spring-context
module configures a StandardTypeLocator using the bean ClassLoader of
the corresponding BeanFactory.

This commit improves the documentation to raise awareness of this fact.

Closes gh-26253
This commit is contained in:
Sam Brannen 2023-09-08 17:12:16 +02:00
parent 1227fe5774
commit 10de295a72
3 changed files with 46 additions and 6 deletions

View File

@ -38,5 +38,15 @@ Kotlin::
---- ----
====== ======
[NOTE]
====
If your application or framework manages its own `EvaluationContext`, you may need to
manually configure a `StandardTypeLocator` with a specific `ClassLoader` to ensure that
the SpEL expression parser is able to reliably locate user types.
For example, the `StandardBeanExpressionResolver` in the `spring-context` module
configures a `StandardTypeLocator` using the bean `ClassLoader` of the corresponding
`BeanFactory`.
====

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2018 the original author or authors. * Copyright 2002-2023 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -39,12 +39,17 @@ import org.springframework.util.Assert;
/** /**
* A powerful and highly configurable {@link EvaluationContext} implementation. * A powerful and highly configurable {@link EvaluationContext} implementation.
* This context uses standard implementations of all applicable strategies,
* based on reflection to resolve properties, methods and fields.
* *
* <p>For a simpler builder-style context variant for data-binding purposes, * <p>This context uses standard implementations of all applicable strategies,
* based on reflection to resolve properties, methods, and fields. Note, however,
* that you may need to manually configure a {@code StandardTypeLocator} with a
* specific {@link ClassLoader} to ensure that the SpEL expression parser is able
* to reliably locate user types. See {@link #setTypeLocator(TypeLocator)} for
* details.
*
* <p>For a simpler, builder-style context variant for data-binding purposes,
* consider using {@link SimpleEvaluationContext} instead which allows for * consider using {@link SimpleEvaluationContext} instead which allows for
* opting into several SpEL features as needed by specific evaluation cases. * opting into several SpEL features as needed by specific use cases.
* *
* @author Andy Clement * @author Andy Clement
* @author Juergen Hoeller * @author Juergen Hoeller
@ -182,11 +187,29 @@ public class StandardEvaluationContext implements EvaluationContext {
return this.beanResolver; return this.beanResolver;
} }
/**
* Set the {@link TypeLocator} to use to find types, either by short or
* fully-qualified name.
* <p>By default, a {@link StandardTypeLocator} will be used.
* <p><strong>NOTE</strong>: Even if a {@code StandardTypeLocator} is
* sufficient, you may need to manually configure a {@code StandardTypeLocator}
* with a specific {@link ClassLoader} to ensure that the SpEL expression
* parser is able to reliably locate user types.
* @param typeLocator the {@code TypeLocator} to use
* @see StandardTypeLocator#StandardTypeLocator(ClassLoader)
* @see #getTypeLocator()
*/
public void setTypeLocator(TypeLocator typeLocator) { public void setTypeLocator(TypeLocator typeLocator) {
Assert.notNull(typeLocator, "TypeLocator must not be null"); Assert.notNull(typeLocator, "TypeLocator must not be null");
this.typeLocator = typeLocator; this.typeLocator = typeLocator;
} }
/**
* Get the configured {@link TypeLocator} that will be used to find types,
* either by short or fully-qualified name.
* <p>See {@link #setTypeLocator(TypeLocator)} for further details.
* @see #setTypeLocator(TypeLocator)
*/
@Override @Override
public TypeLocator getTypeLocator() { public TypeLocator getTypeLocator() {
if (this.typeLocator == null) { if (this.typeLocator == null) {

View File

@ -29,7 +29,8 @@ import org.springframework.util.ClassUtils;
/** /**
* A simple implementation of {@link TypeLocator} that uses the default * A simple implementation of {@link TypeLocator} that uses the default
* {@link ClassLoader} or a supplied {@link ClassLoader} to locate types. * {@link #StandardTypeLocator() ClassLoader} or a supplied
* {@link #StandardTypeLocator(ClassLoader) ClassLoader} to locate types.
* *
* <p>Supports <em>well-known</em> packages, registered as * <p>Supports <em>well-known</em> packages, registered as
* {@linkplain #registerImport(String) import prefixes}. If a type cannot be found, * {@linkplain #registerImport(String) import prefixes}. If a type cannot be found,
@ -51,6 +52,9 @@ public class StandardTypeLocator implements TypeLocator {
/** /**
* Create a {@code StandardTypeLocator} for the default {@link ClassLoader} * Create a {@code StandardTypeLocator} for the default {@link ClassLoader}
* (typically, the thread context {@code ClassLoader}). * (typically, the thread context {@code ClassLoader}).
* <p>Favor {@link #StandardTypeLocator(ClassLoader)} over this constructor
* in order to provide a specific {@link ClassLoader} that is able to reliably
* locate user types.
* @see ClassUtils#getDefaultClassLoader() * @see ClassUtils#getDefaultClassLoader()
*/ */
public StandardTypeLocator() { public StandardTypeLocator() {
@ -59,6 +63,9 @@ public class StandardTypeLocator implements TypeLocator {
/** /**
* Create a {@code StandardTypeLocator} for the given {@link ClassLoader}. * Create a {@code StandardTypeLocator} for the given {@link ClassLoader}.
* <p>Favor this constructor over {@link #StandardTypeLocator()} in order
* to provide a specific {@link ClassLoader} that is able to reliably locate
* user types.
* @param classLoader the {@code ClassLoader} to delegate to * @param classLoader the {@code ClassLoader} to delegate to
*/ */
public StandardTypeLocator(@Nullable ClassLoader classLoader) { public StandardTypeLocator(@Nullable ClassLoader classLoader) {