DATACMNS-1114 - Introduced usage of nullable annotations for API validation.

Marked all packages with Spring Frameworks @NonNullApi. Added Spring's @Nullable to methods, parameters and fields that take or produce null values. Adapted using code to make sure the IDE can evaluate the null flow properly. Fixed Javadoc in places where an invalid null handling policy was advertised. Strengthened null requirements for types that expose null-instances.

Removed null handling from converters for JodaTime and ThreeTenBP. Introduced factory methods Page.empty() and Page.empty(Pageable). Introduced default methods getRequiredGetter(), …Setter() and …Field() on PersistentProperty to allow non-nullable lookups of members. The same for TypeInformation.getrequiredActualType(), …SuperTypeInformation().

Tweaked PersistentPropertyCreator.addPropertiesForRemainingDescriptors() to filter unsuitable PropertyDescriptors before actually trying to create a Property instance from them as the new stronger nullability requirements would cause exceptions downstream.

Lazy.get() now expects a non-null return value. Clients being able to cope with null need to call ….orElse(…).

Original pull request: #232.
This commit is contained in:
Oliver Gierke
2017-06-27 08:41:16 +02:00
parent d9b16d8a27
commit 049970874d
234 changed files with 2274 additions and 1179 deletions

View File

@@ -44,10 +44,14 @@ public final class Accessor {
Assert.notNull(method, "Method must not be null!");
this.descriptor = BeanUtils.findPropertyForMethod(method);
this.method = method;
PropertyDescriptor descriptor = BeanUtils.findPropertyForMethod(method);
Assert.notNull(descriptor, () -> String.format("Invoked method %s is no accessor method!", method));
if (descriptor == null) {
throw new IllegalArgumentException(String.format("Invoked method %s is no accessor method!", method));
}
this.descriptor = descriptor;
this.method = method;
}
/**

View File

@@ -25,6 +25,8 @@ import java.util.Arrays;
import java.util.Map;
import java.util.Optional;
import javax.annotation.Nullable;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.ProxyMethodInvocation;
@@ -48,8 +50,9 @@ public class DefaultMethodInvokingMethodInterceptor implements MethodInterceptor
* (non-Javadoc)
* @see org.aopalliance.intercept.MethodInterceptor#invoke(org.aopalliance.intercept.MethodInvocation)
*/
@Nullable
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
public Object invoke(@SuppressWarnings("null") MethodInvocation invocation) throws Throwable {
Method method = invocation.getMethod();

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2015 the original author or authors.
* Copyright 2015-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,6 +21,8 @@ import lombok.RequiredArgsConstructor;
import java.lang.reflect.Method;
import java.util.Map;
import javax.annotation.Nullable;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.util.ReflectionUtils;
@@ -40,8 +42,9 @@ class MapAccessingMethodInterceptor implements MethodInterceptor {
* (non-Javadoc)
* @see org.aopalliance.intercept.MethodInterceptor#invoke(org.aopalliance.intercept.MethodInvocation)
*/
@Nullable
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
public Object invoke(@SuppressWarnings("null") MethodInvocation invocation) throws Throwable {
Method method = invocation.getMethod();

View File

@@ -26,12 +26,15 @@ import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.annotation.Nonnull;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.core.CollectionFactory;
import org.springframework.core.convert.ConversionService;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.data.util.TypeInformation;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
@@ -54,8 +57,9 @@ class ProjectingMethodInterceptor implements MethodInterceptor {
* (non-Javadoc)
* @see org.aopalliance.intercept.MethodInterceptor#invoke(org.aopalliance.intercept.MethodInvocation)
*/
@Nullable
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
public Object invoke(@SuppressWarnings("null") @Nonnull MethodInvocation invocation) throws Throwable {
Object result = delegate.invoke(invocation);
@@ -121,6 +125,7 @@ class ProjectingMethodInterceptor implements MethodInterceptor {
return result;
}
@Nullable
private Object getProjection(Object result, Class<?> returnType) {
return result == null || ClassUtils.isAssignable(returnType, result.getClass()) ? result
: factory.createProjection(returnType, result);

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.data.projection;
import org.springframework.lang.Nullable;
/**
* A factory to create projecting instances for other objects usually used to allow easy creation of representation
* projections to define which properties of a domain objects shall be exported in which way.
@@ -29,11 +31,23 @@ public interface ProjectionFactory {
* the implementations.
*
* @param projectionType the type to create, must not be {@literal null}.
* @param source the object to create a projection for, can be {@literal null}
* @param source the object to create a projection for, must not be {@literal null}.
* @return
*/
<T> T createProjection(Class<T> projectionType, Object source);
/**
* Creates a projection to the given type for the given nullable source.
*
* @param projectionType must not be {@literal null}.
* @param source can be {@literal null}.
* @return
*/
@Nullable
default <T> T createNullableProjection(Class<T> projectionType, @Nullable Object source) {
return source == null ? null : createProjection(projectionType, source);
}
/**
* Creates a projection instance for the given type.
*

View File

@@ -23,6 +23,7 @@ import org.aopalliance.intercept.MethodInvocation;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.data.util.DirectFieldAccessFallbackBeanWrapper;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
@@ -52,8 +53,9 @@ class PropertyAccessingMethodInterceptor implements MethodInterceptor {
* (non-Javadoc)
* @see org.aopalliance.intercept.MethodInterceptor#invoke(org.aopalliance.intercept.MethodInvocation)
*/
@Nullable
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
public Object invoke(@SuppressWarnings("null") MethodInvocation invocation) throws Throwable {
Method method = invocation.getMethod();

View File

@@ -28,6 +28,7 @@ import org.springframework.aop.framework.ProxyFactory;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ConcurrentReferenceHashMap;
@@ -48,7 +49,7 @@ class ProxyProjectionFactory implements ProjectionFactory, BeanClassLoaderAware
private final List<MethodInterceptorFactory> factories;
private final ConversionService conversionService;
private final Map<Class<?>, ProjectionInformation> projectionInformationCache = new ConcurrentReferenceHashMap<>();
private ClassLoader classLoader;
private @Nullable ClassLoader classLoader;
/**
* Creates a new {@link ProxyProjectionFactory}.
@@ -94,9 +95,10 @@ class ProxyProjectionFactory implements ProjectionFactory, BeanClassLoaderAware
public <T> T createProjection(Class<T> projectionType, Object source) {
Assert.notNull(projectionType, "Projection type must not be null!");
Assert.notNull(source, "Source must not be null!");
Assert.isTrue(projectionType.isInterface(), "Projection type must be an interface!");
if (source == null || projectionType.isInstance(source)) {
if (projectionType.isInstance(source)) {
return (T) source;
}
@@ -229,8 +231,9 @@ class ProxyProjectionFactory implements ProjectionFactory, BeanClassLoaderAware
* (non-Javadoc)
* @see org.aopalliance.intercept.MethodInterceptor#invoke(org.aopalliance.intercept.MethodInvocation)
*/
@Nullable
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
public Object invoke(@SuppressWarnings("null") MethodInvocation invocation) throws Throwable {
if (invocation.getMethod().equals(GET_TARGET_CLASS_METHOD)) {
return targetType;

View File

@@ -28,6 +28,7 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.data.util.AnnotationDetectionMethodCallback;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
@@ -45,7 +46,7 @@ public class SpelAwareProxyProjectionFactory extends ProxyProjectionFactory impl
private final Map<Class<?>, Boolean> typeCache = new HashMap<>();
private final SpelExpressionParser parser = new SpelExpressionParser();
private BeanFactory beanFactory;
private @Nullable BeanFactory beanFactory;
/*
* (non-Javadoc)

View File

@@ -32,6 +32,7 @@ import org.springframework.expression.ParserContext;
import org.springframework.expression.common.TemplateParserContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
@@ -63,7 +64,7 @@ class SpelEvaluatingMethodInterceptor implements MethodInterceptor {
* @param parser must not be {@literal null}.
* @param targetInterface must not be {@literal null}.
*/
public SpelEvaluatingMethodInterceptor(MethodInterceptor delegate, Object target, BeanFactory beanFactory,
public SpelEvaluatingMethodInterceptor(MethodInterceptor delegate, Object target, @Nullable BeanFactory beanFactory,
SpelExpressionParser parser, Class<?> targetInterface) {
Assert.notNull(delegate, "Delegate MethodInterceptor must not be null!");
@@ -123,8 +124,9 @@ class SpelEvaluatingMethodInterceptor implements MethodInterceptor {
* (non-Javadoc)
* @see org.aopalliance.intercept.MethodInterceptor#invoke(org.aopalliance.intercept.MethodInvocation)
*/
@Nullable
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
public Object invoke(@SuppressWarnings("null") MethodInvocation invocation) throws Throwable {
Expression expression = expressions.get(invocation.getMethod().hashCode());

View File

@@ -17,6 +17,7 @@ package org.springframework.data.projection;
import org.springframework.aop.RawTargetAccess;
import org.springframework.core.DecoratingProxy;
import org.springframework.lang.Nullable;
import com.fasterxml.jackson.annotation.JsonIgnore;
@@ -30,8 +31,9 @@ public interface TargetAware extends org.springframework.aop.TargetClassAware, R
/**
* Returns the type of the proxy target.
*
* @return will never be {@literal null}.
* @return can be {@literal null}.
*/
@Nullable
@JsonIgnore
Class<?> getTargetClass();

View File

@@ -0,0 +1,5 @@
/**
* Projection subsystem.
*/
@org.springframework.lang.NonNullApi
package org.springframework.data.projection;