Refine null-safety based on IDEA warnings

See gh-28797
This commit is contained in:
Sébastien Deleuze 2025-01-22 12:20:41 +01:00
parent 4189f8b477
commit 7f21443a1b
22 changed files with 61 additions and 47 deletions

View File

@ -18,6 +18,8 @@ package org.springframework.aop;
import java.lang.reflect.Method;
import org.jspecify.annotations.Nullable;
/**
* Part of a {@link Pointcut}: Checks whether the target method is eligible for advice.
*
@ -94,7 +96,7 @@ public interface MethodMatcher {
* @return whether there's a runtime match
* @see #matches(Method, Class)
*/
boolean matches(Method method, Class<?> targetClass, Object... args);
boolean matches(Method method, Class<?> targetClass, @Nullable Object... args);
/**

View File

@ -19,6 +19,8 @@ package org.springframework.aop;
import java.io.Serializable;
import java.lang.reflect.Method;
import org.jspecify.annotations.Nullable;
/**
* Canonical MethodMatcher instance that matches all methods.
*
@ -48,7 +50,7 @@ final class TrueMethodMatcher implements MethodMatcher, Serializable {
}
@Override
public boolean matches(Method method, Class<?> targetClass, Object... args) {
public boolean matches(Method method, Class<?> targetClass, @Nullable Object... args) {
// Should never be invoked as isRuntime returns false.
throw new UnsupportedOperationException();
}

View File

@ -340,7 +340,7 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
}
@Override
public boolean matches(Method method, Class<?> targetClass, Object... args) {
public boolean matches(Method method, Class<?> targetClass, @Nullable Object... args) {
ShadowMatch shadowMatch = getTargetShadowMatch(method, targetClass);
// Bind Spring AOP proxy to AspectJ "this" and Spring AOP target to AspectJ target,

View File

@ -289,7 +289,7 @@ final class InstantiationModelAwarePointcutAdvisorImpl
}
@Override
public boolean matches(Method method, Class<?> targetClass, Object... args) {
public boolean matches(Method method, Class<?> targetClass, @Nullable Object... args) {
// This can match only on declared pointcut.
return (isAspectMaterialized() && this.declaredPointcut.matches(method, targetClass, args));
}

View File

@ -144,7 +144,7 @@ public class ControlFlowPointcut implements Pointcut, ClassFilter, MethodMatcher
}
@Override
public boolean matches(Method method, Class<?> targetClass, Object... args) {
public boolean matches(Method method, Class<?> targetClass, @Nullable Object... args) {
incrementEvaluationCount();
for (StackTraceElement element : new Throwable().getStackTrace()) {

View File

@ -151,7 +151,7 @@ public abstract class MethodMatchers {
}
@Override
public boolean matches(Method method, Class<?> targetClass, Object... args) {
public boolean matches(Method method, Class<?> targetClass, @Nullable Object... args) {
return this.mm1.matches(method, targetClass, args) || this.mm2.matches(method, targetClass, args);
}
@ -303,7 +303,7 @@ public abstract class MethodMatchers {
}
@Override
public boolean matches(Method method, Class<?> targetClass, Object... args) {
public boolean matches(Method method, Class<?> targetClass, @Nullable Object... args) {
// Because a dynamic intersection may be composed of a static and dynamic part,
// we must avoid calling the 3-arg matches method on a dynamic matcher, as
// it will probably be an unsupported operation.
@ -373,7 +373,7 @@ public abstract class MethodMatchers {
}
@Override
public boolean matches(Method method, Class<?> targetClass, Object... args) {
public boolean matches(Method method, Class<?> targetClass, @Nullable Object... args) {
return !this.original.matches(method, targetClass, args);
}

View File

@ -18,6 +18,8 @@ package org.springframework.aop.support;
import java.lang.reflect.Method;
import org.jspecify.annotations.Nullable;
import org.springframework.aop.MethodMatcher;
/**
@ -34,7 +36,7 @@ public abstract class StaticMethodMatcher implements MethodMatcher {
}
@Override
public final boolean matches(Method method, Class<?> targetClass, Object... args) {
public final boolean matches(Method method, Class<?> targetClass, @Nullable Object... args) {
// should never be invoked because isRuntime() returns false
throw new UnsupportedOperationException("Illegal MethodMatcher usage");
}

View File

@ -77,7 +77,7 @@ public interface ObjectProvider<T> extends ObjectFactory<T>, Iterable<T> {
* @throws BeansException in case of creation errors
* @see #getObject()
*/
default T getObject(Object... args) throws BeansException {
default T getObject(@Nullable Object... args) throws BeansException {
throw new UnsupportedOperationException("Retrieval with arguments not supported -" +
"for custom ObjectProvider classes, implement getObject(Object...) for your purposes");
}

View File

@ -140,7 +140,7 @@ class ConstructorResolver {
Constructor<?> constructorToUse = null;
ArgumentsHolder argsHolderToUse = null;
Object[] argsToUse = null;
@Nullable Object[] argsToUse = null;
if (explicitArgs != null) {
argsToUse = explicitArgs;
@ -227,7 +227,7 @@ class ConstructorResolver {
Class<?>[] paramTypes = candidate.getParameterTypes();
if (resolvedValues != null) {
try {
String[] paramNames = null;
@Nullable String[] paramNames = null;
if (resolvedValues.containsNamedArgument()) {
paramNames = ConstructorPropertiesChecker.evaluate(candidate, parameterCount);
if (paramNames == null) {
@ -437,7 +437,7 @@ class ConstructorResolver {
argsToUse = explicitArgs;
}
else {
Object[] argsToResolve = null;
@Nullable Object[] argsToResolve = null;
synchronized (mbd.constructorArgumentLock) {
factoryMethodToUse = (Method) mbd.resolvedConstructorOrFactoryMethod;
if (factoryMethodToUse != null && mbd.constructorArgumentsResolved) {
@ -536,7 +536,7 @@ class ConstructorResolver {
else {
// Resolved constructor arguments: type conversion and/or autowiring necessary.
try {
String[] paramNames = null;
@Nullable String[] paramNames = null;
if (resolvedValues != null && resolvedValues.containsNamedArgument()) {
ParameterNameDiscoverer pnd = this.beanFactory.getParameterNameDiscoverer();
if (pnd != null) {
@ -719,7 +719,7 @@ class ConstructorResolver {
*/
private ArgumentsHolder createArgumentArray(
String beanName, RootBeanDefinition mbd, @Nullable ConstructorArgumentValues resolvedValues,
BeanWrapper bw, Class<?>[] paramTypes, String @Nullable [] paramNames, Executable executable,
BeanWrapper bw, Class<?>[] paramTypes, @Nullable String @Nullable [] paramNames, Executable executable,
boolean autowiring, boolean fallback) throws UnsatisfiedDependencyException {
TypeConverter customConverter = this.beanFactory.getCustomTypeConverter();

View File

@ -422,7 +422,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
return resolved;
}
@Override
public T getObject(Object... args) throws BeansException {
public T getObject(@Nullable Object... args) throws BeansException {
T resolved = resolveBean(requiredType, args, false);
if (resolved == null) {
throw new NoSuchBeanDefinitionException(requiredType);
@ -2147,7 +2147,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
* Create an {@link Optional} wrapper for the specified dependency.
*/
private Optional<?> createOptionalDependency(
DependencyDescriptor descriptor, @Nullable String beanName, final Object... args) {
DependencyDescriptor descriptor, @Nullable String beanName, final @Nullable Object... args) {
DependencyDescriptor descriptorToUse = new NestedDependencyDescriptor(descriptor) {
@Override
@ -2317,7 +2317,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
}
@Override
public Object getObject(final Object... args) throws BeansException {
public Object getObject(final @Nullable Object... args) throws BeansException {
if (this.optional) {
return createOptionalDependency(this.descriptor, this.beanName, args);
}

View File

@ -301,7 +301,7 @@ public class StaticListableBeanFactory implements ListableBeanFactory {
}
}
@Override
public T getObject(Object... args) throws BeansException {
public T getObject(@Nullable Object... args) throws BeansException {
String[] beanNames = getBeanNamesForType(requiredType);
if (beanNames.length == 1) {
return (T) getBean(beanNames[0], args);

View File

@ -63,7 +63,7 @@ final class BeanMethod extends ConfigurationMethod {
return;
}
Map<String, Object> attributes =
Map<String, @Nullable Object> attributes =
getConfigurationClass().getMetadata().getAnnotationAttributes(Configuration.class.getName());
if (attributes != null && (Boolean) attributes.get("proxyBeanMethods") && !getMetadata().isOverridable()) {
// instance @Bean methods within @Configuration classes must be overridable to accommodate CGLIB

View File

@ -220,7 +220,7 @@ final class ConfigurationClass {
@SuppressWarnings("NullAway") // Reflection
void validate(ProblemReporter problemReporter) {
Map<String, Object> attributes = this.metadata.getAnnotationAttributes(Configuration.class.getName());
Map<String, @Nullable Object> attributes = this.metadata.getAnnotationAttributes(Configuration.class.getName());
// A configuration class may not be final (CGLIB limitation) unless it declares proxyBeanMethods=false
if (attributes != null && (Boolean) attributes.get("proxyBeanMethods") && this.metadata.isFinal()) {

View File

@ -26,6 +26,7 @@ import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jspecify.annotations.Nullable;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
@ -309,7 +310,7 @@ class ConfigurationClassBeanDefinitionReader {
ccbd.setNonUniqueFactoryMethodName(ccbd.getFactoryMethodMetadata().getMethodName());
return true;
}
Map<String, Object> attributes =
Map<String, @Nullable Object> attributes =
configClass.getMetadata().getAnnotationAttributes(Configuration.class.getName());
if ((attributes != null && (Boolean) attributes.get("enforceUniqueMethods")) ||
!this.registry.isBeanDefinitionOverridable(beanName)) {

View File

@ -16,6 +16,8 @@
package org.springframework.context.annotation;
import org.jspecify.annotations.Nullable;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.util.MultiValueMap;
@ -33,7 +35,7 @@ class ProfileCondition implements Condition {
@Override
@SuppressWarnings("NullAway") // Reflection
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName());
MultiValueMap<String, @Nullable Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName());
if (attrs != null) {
for (Object value : attrs.get("value")) {
if (context.getEnvironment().matchesProfiles((String[]) value)) {

View File

@ -139,7 +139,7 @@ public class AnnotationJmxAttributeSource implements JmxAttributeSource, BeanFac
}
@Override
public org.springframework.jmx.export.metadata.ManagedOperationParameter[] getManagedOperationParameters(Method method)
public org.springframework.jmx.export.metadata.@Nullable ManagedOperationParameter[] getManagedOperationParameters(Method method)
throws InvalidMetadataException {
List<MergedAnnotation<? extends Annotation>> anns = getRepeatableAnnotations(
@ -149,7 +149,7 @@ public class AnnotationJmxAttributeSource implements JmxAttributeSource, BeanFac
}
@Override
public org.springframework.jmx.export.metadata.ManagedNotification[] getManagedNotifications(Class<?> clazz)
public org.springframework.jmx.export.metadata.@Nullable ManagedNotification[] getManagedNotifications(Class<?> clazz)
throws InvalidMetadataException {
List<MergedAnnotation<? extends Annotation>> anns = getRepeatableAnnotations(

View File

@ -18,6 +18,7 @@ package org.springframework.jmx.export.assembler;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.Objects;
import javax.management.Descriptor;
import javax.management.MBeanParameterInfo;
@ -259,7 +260,7 @@ public class MetadataMBeanInfoAssembler extends AbstractReflectiveMBeanInfoAssem
*/
@Override
protected MBeanParameterInfo[] getOperationParameters(Method method, String beanKey) {
ManagedOperationParameter[] params = obtainAttributeSource().getManagedOperationParameters(method);
@Nullable ManagedOperationParameter[] params = obtainAttributeSource().getManagedOperationParameters(method);
if (ObjectUtils.isEmpty(params)) {
return super.getOperationParameters(method, beanKey);
}
@ -267,7 +268,7 @@ public class MetadataMBeanInfoAssembler extends AbstractReflectiveMBeanInfoAssem
MBeanParameterInfo[] parameterInfo = new MBeanParameterInfo[params.length];
Class<?>[] methodParameters = method.getParameterTypes();
for (int i = 0; i < params.length; i++) {
ManagedOperationParameter param = params[i];
ManagedOperationParameter param = Objects.requireNonNull(params[i]);
parameterInfo[i] =
new MBeanParameterInfo(param.getName(), methodParameters[i].getName(), param.getDescription());
}
@ -280,14 +281,14 @@ public class MetadataMBeanInfoAssembler extends AbstractReflectiveMBeanInfoAssem
*/
@Override
protected ModelMBeanNotificationInfo[] getNotificationInfo(Object managedBean, String beanKey) {
ManagedNotification[] notificationAttributes =
@Nullable ManagedNotification[] notificationAttributes =
obtainAttributeSource().getManagedNotifications(getClassToExpose(managedBean));
ModelMBeanNotificationInfo[] notificationInfos =
new ModelMBeanNotificationInfo[notificationAttributes.length];
for (int i = 0; i < notificationAttributes.length; i++) {
ManagedNotification attribute = notificationAttributes[i];
notificationInfos[i] = JmxMetadataUtils.convertToModelMBeanNotificationInfo(attribute);
notificationInfos[i] = JmxMetadataUtils.convertToModelMBeanNotificationInfo(Objects.requireNonNull(attribute));
}
return notificationInfos;

View File

@ -80,7 +80,7 @@ public interface JmxAttributeSource {
* @return the parameter information.
* @throws InvalidMetadataException in the case of invalid attributes.
*/
ManagedOperationParameter[] getManagedOperationParameters(Method method) throws InvalidMetadataException;
@Nullable ManagedOperationParameter[] getManagedOperationParameters(Method method) throws InvalidMetadataException;
/**
* Implementations should return an array of {@link ManagedNotification ManagedNotifications}
@ -90,7 +90,7 @@ public interface JmxAttributeSource {
* @return the notification information
* @throws InvalidMetadataException in the case of invalid metadata
*/
ManagedNotification[] getManagedNotifications(Class<?> clazz) throws InvalidMetadataException;
@Nullable ManagedNotification[] getManagedNotifications(Class<?> clazz) throws InvalidMetadataException;

View File

@ -162,7 +162,7 @@ public class SimpleJndiBeanFactory extends JndiLocatorSupport implements BeanFac
return getBean(requiredType);
}
@Override
public T getObject(Object... args) throws BeansException {
public T getObject(@Nullable Object... args) throws BeansException {
return getBean(requiredType, args);
}
@Override

View File

@ -16,6 +16,8 @@
package org.springframework.expression;
import org.jspecify.annotations.Nullable;
/**
* A {@code ConstructorExecutor} is built by a {@link ConstructorResolver} and
* can be cached by the infrastructure to repeat an operation quickly without
@ -49,6 +51,6 @@ public interface ConstructorExecutor {
* @throws AccessException if there is a problem executing the constructor or
* if this {@code ConstructorExecutor} has become stale
*/
TypedValue execute(EvaluationContext context, Object... arguments) throws AccessException;
TypedValue execute(EvaluationContext context, @Nullable Object... arguments) throws AccessException;
}

View File

@ -16,6 +16,8 @@
package org.springframework.expression;
import org.jspecify.annotations.Nullable;
/**
* A {@code MethodExecutor} is built by a {@link MethodResolver} and can be cached
* by the infrastructure to repeat an operation quickly without going back to the
@ -50,6 +52,6 @@ public interface MethodExecutor {
* @throws AccessException if there is a problem executing the method or
* if this {@code MethodExecutor} has become stale
*/
TypedValue execute(EvaluationContext context, Object target, Object... arguments) throws AccessException;
TypedValue execute(EvaluationContext context, Object target, @Nullable Object... arguments) throws AccessException;
}

View File

@ -69,12 +69,12 @@ class HeadersAdaptersBaseline {
}
@Override
public void addAll(String key, List<? extends String> values) {
public void addAll(String key, List<? extends @Nullable String> values) {
values.forEach(value -> add(key, value));
}
@Override
public void addAll(MultiValueMap<String, String> values) {
public void addAll(MultiValueMap<String, @Nullable String> values) {
values.forEach(this::addAll);
}
@ -84,12 +84,12 @@ class HeadersAdaptersBaseline {
}
@Override
public void setAll(Map<String, String> values) {
public void setAll(Map<String, @Nullable String> values) {
values.forEach(this::set);
}
@Override
public Map<String, String> toSingleValueMap() {
public Map<String, @Nullable String> toSingleValueMap() {
Map<String, String> map = CollectionUtils.newLinkedHashMap(size());
this.message.headerIterator().forEachRemaining(h -> map.putIfAbsent(h.getName(), h.getValue()));
return map;
@ -274,12 +274,12 @@ class HeadersAdaptersBaseline {
}
@Override
public void addAll(String key, List<? extends String> values) {
public void addAll(String key, List<? extends @Nullable String> values) {
values.forEach(value -> add(key, value));
}
@Override
public void addAll(MultiValueMap<String, String> values) {
public void addAll(MultiValueMap<String, @Nullable String> values) {
values.forEach(this::addAll);
}
@ -295,7 +295,7 @@ class HeadersAdaptersBaseline {
}
@Override
public void setAll(Map<String, String> values) {
public void setAll(Map<String, @Nullable String> values) {
values.forEach(this::set);
}
@ -566,12 +566,12 @@ class HeadersAdaptersBaseline {
}
@Override
public void addAll(String key, List<? extends String> values) {
public void addAll(String key, List<? extends @Nullable String> values) {
this.headers.add(key, values);
}
@Override
public void addAll(MultiValueMap<String, String> values) {
public void addAll(MultiValueMap<String, @Nullable String> values) {
values.forEach(this.headers::add);
}
@ -583,7 +583,7 @@ class HeadersAdaptersBaseline {
}
@Override
public void setAll(Map<String, String> values) {
public void setAll(Map<String, @Nullable String> values) {
values.forEach(this.headers::set);
}
@ -809,12 +809,12 @@ class HeadersAdaptersBaseline {
}
@Override
public void addAll(String key, List<? extends String> values) {
public void addAll(String key, List<? extends @Nullable String> values) {
this.headers.add(key, values);
}
@Override
public void addAll(MultiValueMap<String, String> values) {
public void addAll(MultiValueMap<String, @Nullable String> values) {
values.forEach(this.headers::add);
}
@ -826,7 +826,7 @@ class HeadersAdaptersBaseline {
}
@Override
public void setAll(Map<String, String> values) {
public void setAll(Map<String, @Nullable String> values) {
values.forEach(this.headers::set);
}