Introduce null-safety of Spring Framework API
This commit introduces 2 new @Nullable and @NonNullApi annotations that leverage JSR 305 (dormant but available via Findbugs jsr305 dependency and already used by libraries like OkHttp) meta-annotations to specify explicitly null-safety of Spring Framework parameters and return values. In order to avoid adding too much annotations, the default is set at package level with @NonNullApi and @Nullable annotations are added when needed at parameter or return value level. These annotations are intended to be used on Spring Framework itself but also by other Spring projects. @Nullable annotations have been introduced based on Javadoc and search of patterns like "return null;". It is expected that nullability of Spring Framework API will be polished with complementary commits. In practice, this will make the whole Spring Framework API null-safe for Kotlin projects (when KT-10942 will be fixed) since Kotlin will be able to leverage these annotations to know if a parameter or a return value is nullable or not. But this is also useful for Java developers as well since IntelliJ IDEA, for example, also understands these annotations to generate warnings when unsafe nullable usages are detected. Issue: SPR-15540
This commit is contained in:
@@ -29,6 +29,7 @@ import com.github.benmanes.caffeine.cache.CaffeineSpec;
|
||||
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
@@ -88,7 +89,7 @@ public class CaffeineCacheManager implements CacheManager {
|
||||
* <p>Calling this with a {@code null} collection argument resets the
|
||||
* mode to 'dynamic', allowing for further creation of caches again.
|
||||
*/
|
||||
public void setCacheNames(Collection<String> cacheNames) {
|
||||
public void setCacheNames(@Nullable Collection<String> cacheNames) {
|
||||
if (cacheNames != null) {
|
||||
for (String name : cacheNames) {
|
||||
this.cacheMap.put(name, createCaffeineCache(name));
|
||||
|
||||
@@ -3,4 +3,7 @@
|
||||
* <a href="https://github.com/ben-manes/caffeine/">Caffeine</a> library,
|
||||
* allowing to set up Caffeine caches within Spring's cache abstraction.
|
||||
*/
|
||||
@NonNullApi
|
||||
package org.springframework.cache.caffeine;
|
||||
|
||||
import org.springframework.lang.NonNullApi;
|
||||
@@ -9,4 +9,7 @@
|
||||
* Instead, consider using it through JCache (JSR-107), with
|
||||
* Spring's support in {@code org.springframework.cache.jcache}.
|
||||
*/
|
||||
@NonNullApi
|
||||
package org.springframework.cache.ehcache;
|
||||
|
||||
import org.springframework.lang.NonNullApi;
|
||||
@@ -25,6 +25,7 @@ import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* {@link FactoryBean} for a JCache {@link javax.cache.CacheManager},
|
||||
@@ -54,7 +55,7 @@ public class JCacheManagerFactoryBean
|
||||
* Specify the URI for the desired CacheManager.
|
||||
* Default is {@code null} (i.e. JCache's default).
|
||||
*/
|
||||
public void setCacheManagerUri(URI cacheManagerUri) {
|
||||
public void setCacheManagerUri(@Nullable URI cacheManagerUri) {
|
||||
this.cacheManagerUri = cacheManagerUri;
|
||||
}
|
||||
|
||||
@@ -63,7 +64,7 @@ public class JCacheManagerFactoryBean
|
||||
* Default is {@code null} (i.e. no special properties to apply).
|
||||
* @see javax.cache.spi.CachingProvider#getCacheManager(URI, ClassLoader, Properties)
|
||||
*/
|
||||
public void setCacheManagerProperties(Properties cacheManagerProperties) {
|
||||
public void setCacheManagerProperties(@Nullable Properties cacheManagerProperties) {
|
||||
this.cacheManagerProperties = cacheManagerProperties;
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ import org.springframework.cache.interceptor.AbstractCacheInvoker;
|
||||
import org.springframework.cache.interceptor.CacheErrorHandler;
|
||||
import org.springframework.cache.interceptor.CacheOperationInvocationContext;
|
||||
import org.springframework.cache.interceptor.CacheOperationInvoker;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
@@ -71,6 +72,7 @@ abstract class AbstractCacheInterceptor<O extends AbstractJCacheOperation<A>, A
|
||||
* <p>Throw an {@link IllegalStateException} if the collection holds more than one element
|
||||
* @return the single element or {@code null} if the collection is empty
|
||||
*/
|
||||
@Nullable
|
||||
static Cache extractFrom(Collection<? extends Cache> caches) {
|
||||
if (CollectionUtils.isEmpty(caches)) {
|
||||
return null;
|
||||
|
||||
@@ -26,6 +26,7 @@ import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.core.BridgeMethodResolver;
|
||||
import org.springframework.core.MethodClassKey;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
@@ -77,6 +78,7 @@ public abstract class AbstractFallbackJCacheOperationSource implements JCacheOpe
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private JCacheOperation<?> computeCacheOperation(Method method, Class<?> targetClass) {
|
||||
// Don't allow no-public methods as required.
|
||||
if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
|
||||
@@ -113,6 +115,7 @@ public abstract class AbstractFallbackJCacheOperationSource implements JCacheOpe
|
||||
* @return the cache operation associated with this method
|
||||
* (or {@code null} if none)
|
||||
*/
|
||||
@Nullable
|
||||
protected abstract JCacheOperation<?> findCacheOperation(Method method, Class<?> targetType);
|
||||
|
||||
/**
|
||||
|
||||
@@ -31,6 +31,7 @@ import javax.cache.annotation.CacheResult;
|
||||
|
||||
import org.springframework.cache.interceptor.CacheResolver;
|
||||
import org.springframework.cache.interceptor.KeyGenerator;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -157,6 +158,7 @@ public abstract class AnnotationJCacheOperationSource extends AbstractFallbackJC
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected CacheResolverFactory determineCacheResolverFactory(CacheDefaults defaults,
|
||||
Class<? extends CacheResolverFactory> candidate) {
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ import org.springframework.cache.interceptor.CacheErrorHandler;
|
||||
import org.springframework.cache.interceptor.CacheOperationInvocationContext;
|
||||
import org.springframework.cache.interceptor.CacheOperationInvoker;
|
||||
import org.springframework.cache.interceptor.CacheResolver;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ExceptionTypeFilter;
|
||||
import org.springframework.util.SerializationUtils;
|
||||
|
||||
@@ -92,6 +93,7 @@ class CacheResultInterceptor extends AbstractKeyCacheInterceptor<CacheResultOper
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Cache resolveExceptionCache(CacheOperationInvocationContext<CacheResultOperation> context) {
|
||||
CacheResolver exceptionCacheResolver = context.getOperation().getExceptionCacheResolver();
|
||||
if (exceptionCacheResolver != null) {
|
||||
|
||||
@@ -21,6 +21,7 @@ import javax.cache.annotation.CacheResult;
|
||||
|
||||
import org.springframework.cache.interceptor.CacheResolver;
|
||||
import org.springframework.cache.interceptor.KeyGenerator;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ExceptionTypeFilter;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@@ -78,6 +79,7 @@ class CacheResultOperation extends AbstractJCacheKeyOperation<CacheResult> {
|
||||
* caching exceptions should be disabled.
|
||||
* @see javax.cache.annotation.CacheResult#exceptionCacheName()
|
||||
*/
|
||||
@Nullable
|
||||
public String getExceptionCacheName() {
|
||||
return this.exceptionCacheName;
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ import org.springframework.cache.interceptor.CacheResolver;
|
||||
import org.springframework.cache.interceptor.KeyGenerator;
|
||||
import org.springframework.cache.interceptor.SimpleCacheResolver;
|
||||
import org.springframework.cache.interceptor.SimpleKeyGenerator;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -69,6 +70,7 @@ public class DefaultJCacheOperationSource extends AnnotationJCacheOperationSourc
|
||||
/**
|
||||
* Return the specified cache manager to use, if any.
|
||||
*/
|
||||
@Nullable
|
||||
public CacheManager getCacheManager() {
|
||||
return this.cacheManager;
|
||||
}
|
||||
@@ -84,6 +86,7 @@ public class DefaultJCacheOperationSource extends AnnotationJCacheOperationSourc
|
||||
/**
|
||||
* Return the specified cache resolver to use, if any.
|
||||
*/
|
||||
@Nullable
|
||||
public CacheResolver getCacheResolver() {
|
||||
return this.cacheResolver;
|
||||
}
|
||||
@@ -99,6 +102,7 @@ public class DefaultJCacheOperationSource extends AnnotationJCacheOperationSourc
|
||||
/**
|
||||
* Return the specified exception cache resolver to use, if any.
|
||||
*/
|
||||
@Nullable
|
||||
public CacheResolver getExceptionCacheResolver() {
|
||||
return this.exceptionCacheResolver;
|
||||
}
|
||||
@@ -115,6 +119,7 @@ public class DefaultJCacheOperationSource extends AnnotationJCacheOperationSourc
|
||||
/**
|
||||
* Return the specified key generator to use, if any.
|
||||
*/
|
||||
@Nullable
|
||||
public KeyGenerator getKeyGenerator() {
|
||||
return this.keyGenerator;
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@ package org.springframework.cache.jcache.interceptor;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Interface used by {@link JCacheInterceptor}. Implementations know how to source
|
||||
* cache operation attributes from standard JSR-107 annotations.
|
||||
@@ -37,6 +39,7 @@ public interface JCacheOperationSource {
|
||||
* the declaring class of the method must be used)
|
||||
* @return the cache operation for this method, or {@code null} if none found
|
||||
*/
|
||||
JCacheOperation<?> getCacheOperation(Method method, Class<?> targetClass);
|
||||
@Nullable
|
||||
JCacheOperation<?> getCacheOperation(Method method, @Nullable Class<?> targetClass);
|
||||
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import java.io.Serializable;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.springframework.aop.support.StaticMethodMatcherPointcut;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
@@ -43,6 +44,7 @@ public abstract class JCacheOperationSourcePointcut
|
||||
* Obtain the underlying {@link JCacheOperationSource} (may be {@code null}).
|
||||
* To be implemented by subclasses.
|
||||
*/
|
||||
@Nullable
|
||||
protected abstract JCacheOperationSource getCacheOperationSource();
|
||||
|
||||
@Override
|
||||
|
||||
@@ -4,4 +4,7 @@
|
||||
* and {@link org.springframework.cache.Cache Cache} implementation for
|
||||
* use in a Spring context, using a JSR-107 compliant cache provider.
|
||||
*/
|
||||
@NonNullApi
|
||||
package org.springframework.cache.jcache;
|
||||
|
||||
import org.springframework.lang.NonNullApi;
|
||||
@@ -2,4 +2,7 @@
|
||||
* Transaction-aware decorators for the org.springframework.cache package.
|
||||
* Provides synchronization of put operations with Spring-managed transactions.
|
||||
*/
|
||||
@NonNullApi
|
||||
package org.springframework.cache.transaction;
|
||||
|
||||
import org.springframework.lang.NonNullApi;
|
||||
@@ -25,6 +25,7 @@ import javax.activation.MimetypesFileTypeMap;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Spring-configurable {@code FileTypeMap} implementation that will read
|
||||
@@ -139,7 +140,7 @@ public class ConfigurableMimeFileTypeMap extends FileTypeMap implements Initiali
|
||||
* @see javax.activation.MimetypesFileTypeMap#MimetypesFileTypeMap(java.io.InputStream)
|
||||
* @see javax.activation.MimetypesFileTypeMap#addMimeTypes(String)
|
||||
*/
|
||||
protected FileTypeMap createFileTypeMap(Resource mappingLocation, String[] mappings) throws IOException {
|
||||
protected FileTypeMap createFileTypeMap(@Nullable Resource mappingLocation, @Nullable String[] mappings) throws IOException {
|
||||
MimetypesFileTypeMap fileTypeMap = null;
|
||||
if (mappingLocation != null) {
|
||||
InputStream is = mappingLocation.getInputStream();
|
||||
|
||||
@@ -31,6 +31,7 @@ import javax.mail.Session;
|
||||
import javax.mail.Transport;
|
||||
import javax.mail.internet.MimeMessage;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.mail.MailAuthenticationException;
|
||||
import org.springframework.mail.MailException;
|
||||
import org.springframework.mail.MailParseException;
|
||||
@@ -218,6 +219,7 @@ public class JavaMailSenderImpl implements JavaMailSender {
|
||||
/**
|
||||
* Return the username for the account at the mail host.
|
||||
*/
|
||||
@Nullable
|
||||
public String getUsername() {
|
||||
return this.username;
|
||||
}
|
||||
@@ -240,6 +242,7 @@ public class JavaMailSenderImpl implements JavaMailSender {
|
||||
/**
|
||||
* Return the password for the account at the mail host.
|
||||
*/
|
||||
@Nullable
|
||||
public String getPassword() {
|
||||
return this.password;
|
||||
}
|
||||
@@ -257,6 +260,7 @@ public class JavaMailSenderImpl implements JavaMailSender {
|
||||
* Return the default encoding for {@link MimeMessage MimeMessages},
|
||||
* or {@code null} if none.
|
||||
*/
|
||||
@Nullable
|
||||
public String getDefaultEncoding() {
|
||||
return this.defaultEncoding;
|
||||
}
|
||||
@@ -282,6 +286,7 @@ public class JavaMailSenderImpl implements JavaMailSender {
|
||||
* Return the default Java Activation {@link FileTypeMap} for
|
||||
* {@link MimeMessage MimeMessages}, or {@code null} if none.
|
||||
*/
|
||||
@Nullable
|
||||
public FileTypeMap getDefaultFileTypeMap() {
|
||||
return this.defaultFileTypeMap;
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ import javax.mail.internet.MimeUtility;
|
||||
|
||||
import org.springframework.core.io.InputStreamSource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -361,7 +362,7 @@ public class MimeMessageHelper {
|
||||
* will be added to (can be the same as the root multipart object, or an element
|
||||
* nested underneath the root multipart element)
|
||||
*/
|
||||
protected final void setMimeMultiparts(MimeMultipart root, MimeMultipart main) {
|
||||
protected final void setMimeMultiparts(@Nullable MimeMultipart root, MimeMultipart main) {
|
||||
this.rootMimeMultipart = root;
|
||||
this.mimeMultipart = main;
|
||||
}
|
||||
@@ -423,6 +424,7 @@ public class MimeMessageHelper {
|
||||
* @return the default encoding associated with the MimeMessage,
|
||||
* or {@code null} if none found
|
||||
*/
|
||||
@Nullable
|
||||
protected String getDefaultEncoding(MimeMessage mimeMessage) {
|
||||
if (mimeMessage instanceof SmartMimeMessage) {
|
||||
return ((SmartMimeMessage) mimeMessage).getDefaultEncoding();
|
||||
@@ -433,6 +435,7 @@ public class MimeMessageHelper {
|
||||
/**
|
||||
* Return the specific character encoding used for this message, if any.
|
||||
*/
|
||||
@Nullable
|
||||
public String getEncoding() {
|
||||
return this.encoding;
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@ import javax.activation.FileTypeMap;
|
||||
import javax.mail.Session;
|
||||
import javax.mail.internet.MimeMessage;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Special subclass of the standard JavaMail {@link MimeMessage}, carrying a
|
||||
* default encoding to be used when populating the message and a default Java
|
||||
@@ -48,7 +50,7 @@ class SmartMimeMessage extends MimeMessage {
|
||||
* @param defaultEncoding the default encoding, or {@code null} if none
|
||||
* @param defaultFileTypeMap the default FileTypeMap, or {@code null} if none
|
||||
*/
|
||||
public SmartMimeMessage(Session session, String defaultEncoding, FileTypeMap defaultFileTypeMap) {
|
||||
public SmartMimeMessage(Session session, @Nullable String defaultEncoding, @Nullable FileTypeMap defaultFileTypeMap) {
|
||||
super(session);
|
||||
this.defaultEncoding = defaultEncoding;
|
||||
this.defaultFileTypeMap = defaultFileTypeMap;
|
||||
@@ -58,6 +60,7 @@ class SmartMimeMessage extends MimeMessage {
|
||||
/**
|
||||
* Return the default encoding of this message, or {@code null} if none.
|
||||
*/
|
||||
@Nullable
|
||||
public final String getDefaultEncoding() {
|
||||
return this.defaultEncoding;
|
||||
}
|
||||
@@ -65,6 +68,7 @@ class SmartMimeMessage extends MimeMessage {
|
||||
/**
|
||||
* Return the default FileTypeMap of this message, or {@code null} if none.
|
||||
*/
|
||||
@Nullable
|
||||
public final FileTypeMap getDefaultFileTypeMap() {
|
||||
return this.defaultFileTypeMap;
|
||||
}
|
||||
|
||||
@@ -3,4 +3,7 @@
|
||||
* Provides an extended JavaMailSender interface and a MimeMessageHelper
|
||||
* class for convenient population of a JavaMail MimeMessage.
|
||||
*/
|
||||
@NonNullApi
|
||||
package org.springframework.mail.javamail;
|
||||
|
||||
import org.springframework.lang.NonNullApi;
|
||||
@@ -2,4 +2,7 @@
|
||||
* Spring's generic mail infrastructure.
|
||||
* Concrete implementations are provided in the subpackages.
|
||||
*/
|
||||
@NonNullApi
|
||||
package org.springframework.mail;
|
||||
|
||||
import org.springframework.lang.NonNullApi;
|
||||
@@ -25,6 +25,7 @@ import java.util.concurrent.TimeUnit;
|
||||
import commonj.timers.Timer;
|
||||
import commonj.timers.TimerListener;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.scheduling.TaskScheduler;
|
||||
import org.springframework.scheduling.Trigger;
|
||||
import org.springframework.scheduling.support.SimpleTriggerContext;
|
||||
@@ -164,6 +165,7 @@ public class TimerManagerTaskScheduler extends TimerManagerAccessor implements T
|
||||
this.trigger = trigger;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ScheduledFuture<?> schedule() {
|
||||
this.scheduledExecutionTime = this.trigger.nextExecutionTime(this.triggerContext);
|
||||
if (this.scheduledExecutionTime == null) {
|
||||
|
||||
@@ -2,4 +2,7 @@
|
||||
* Convenience classes for scheduling based on the CommonJ WorkManager/TimerManager
|
||||
* facility, as supported by IBM WebSphere 6.0+ and BEA WebLogic 9.0+.
|
||||
*/
|
||||
@NonNullApi
|
||||
package org.springframework.scheduling.commonj;
|
||||
|
||||
import org.springframework.lang.NonNullApi;
|
||||
@@ -27,6 +27,7 @@ import org.quartz.spi.ClassLoadHelper;
|
||||
import org.springframework.core.io.DefaultResourceLoader;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Wrapper that adapts from the Quartz {@link ClassLoadHelper} interface
|
||||
@@ -82,6 +83,7 @@ public class ResourceLoaderClassLoadHelper implements ClassLoadHelper {
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public URL getResource(String name) {
|
||||
Resource resource = this.resourceLoader.getResource(name);
|
||||
if (resource.exists()) {
|
||||
@@ -101,6 +103,7 @@ public class ResourceLoaderClassLoadHelper implements ClassLoadHelper {
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public InputStream getResourceAsStream(String name) {
|
||||
Resource resource = this.resourceLoader.getResource(name);
|
||||
if (resource.exists()) {
|
||||
|
||||
@@ -22,6 +22,7 @@ import org.quartz.spi.TriggerFiredBundle;
|
||||
import org.springframework.beans.BeanWrapper;
|
||||
import org.springframework.beans.MutablePropertyValues;
|
||||
import org.springframework.beans.PropertyAccessorFactory;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Subclass of {@link AdaptableJobFactory} that also supports Spring-style
|
||||
@@ -55,7 +56,7 @@ public class SpringBeanJobFactory extends AdaptableJobFactory implements Schedul
|
||||
* ignored if there is no corresponding property found on the particular
|
||||
* job class (all other unknown properties will still trigger an exception).
|
||||
*/
|
||||
public void setIgnoredUnknownProperties(String... ignoredUnknownProperties) {
|
||||
public void setIgnoredUnknownProperties(@Nullable String... ignoredUnknownProperties) {
|
||||
this.ignoredUnknownProperties = ignoredUnknownProperties;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,4 +5,7 @@
|
||||
* Triggers as beans in a Spring context. Also provides
|
||||
* convenience classes for implementing Quartz Jobs.
|
||||
*/
|
||||
@NonNullApi
|
||||
package org.springframework.scheduling.quartz;
|
||||
|
||||
import org.springframework.lang.NonNullApi;
|
||||
@@ -38,6 +38,7 @@ import org.springframework.core.io.DefaultResourceLoader;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.core.io.support.PropertiesLoaderUtils;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
@@ -380,6 +381,7 @@ public class FreeMarkerConfigurationFactory {
|
||||
* @param templateLoaders the final List of TemplateLoader instances
|
||||
* @return the aggregate TemplateLoader
|
||||
*/
|
||||
@Nullable
|
||||
protected TemplateLoader getAggregateTemplateLoader(List<TemplateLoader> templateLoaders) {
|
||||
int loaderCount = templateLoaders.size();
|
||||
switch (loaderCount) {
|
||||
|
||||
@@ -3,4 +3,7 @@
|
||||
* <a href="http://www.freemarker.org">FreeMarker</a>
|
||||
* within a Spring application context.
|
||||
*/
|
||||
@NonNullApi
|
||||
package org.springframework.ui.freemarker;
|
||||
|
||||
import org.springframework.lang.NonNullApi;
|
||||
Reference in New Issue
Block a user