Skip to content

Commit f813712

Browse files
committed
Consistent use of @nullable across the codebase (even for internals)
Beyond just formally declaring the current behavior, this revision actually enforces non-null behavior in selected signatures now, not tolerating null values anymore when not explicitly documented. It also changes some utility methods with historic null-in/null-out tolerance towards enforced non-null return values, making them a proper citizen in non-null assignments. Some issues are left as to-do: in particular a thorough revision of spring-test, and a few tests with unclear failures (ignored as "TODO: NULLABLE") to be sorted out in a follow-up commit. Issue: SPR-15540
1 parent ffc3f6d commit f813712

File tree

1,493 files changed

+10639
-9141
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

1,493 files changed

+10639
-9141
lines changed

spring-aop/src/main/java/org/springframework/aop/Advisor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2012 the original author or authors.
2+
* Copyright 2002-2017 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.

spring-aop/src/main/java/org/springframework/aop/TargetSource.java

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*<
2-
* Copyright 2002-2012 the original author or authors.
2+
* Copyright 2002-2017 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -31,24 +31,23 @@
3131
* {@code TargetSources} directly: this is an AOP framework interface.
3232
*
3333
* @author Rod Johnson
34+
* @author Juergen Hoeller
3435
*/
3536
public interface TargetSource extends TargetClassAware {
3637

3738
/**
3839
* Return the type of targets returned by this {@link TargetSource}.
39-
* <p>Can return {@code null}, although certain usages of a
40-
* {@code TargetSource} might just work with a predetermined
41-
* target class.
40+
* <p>Can return {@code null}, although certain usages of a {@code TargetSource}
41+
* might just work with a predetermined target class.
4242
* @return the type of targets returned by this {@link TargetSource}
4343
*/
4444
@Override
4545
Class<?> getTargetClass();
4646

4747
/**
4848
* Will all calls to {@link #getTarget()} return the same object?
49-
* <p>In that case, there will be no need to invoke
50-
* {@link #releaseTarget(Object)}, and the AOP framework can cache
51-
* the return value of {@link #getTarget()}.
49+
* <p>In that case, there will be no need to invoke {@link #releaseTarget(Object)},
50+
* and the AOP framework can cache the return value of {@link #getTarget()}.
5251
* @return {@code true} if the target is immutable
5352
* @see #getTarget
5453
*/

spring-aop/src/main/java/org/springframework/aop/aspectj/AbstractAspectJAdvice.java

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2016 the original author or authors.
2+
* Copyright 2002-2017 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -208,6 +208,7 @@ public final AspectInstanceFactory getAspectInstanceFactory() {
208208
/**
209209
* Return the ClassLoader for aspect instances.
210210
*/
211+
@Nullable
211212
public final ClassLoader getAspectClassLoader() {
212213
return this.aspectInstanceFactory.getAspectClassLoader();
213214
}
@@ -296,8 +297,8 @@ protected void setReturningNameNoCheck(String name) {
296297
}
297298
catch (Throwable ex) {
298299
throw new IllegalArgumentException("Returning name '" + name +
299-
"' is neither a valid argument name nor the fully-qualified name of a Java type on the classpath. " +
300-
"Root cause: " + ex);
300+
"' is neither a valid argument name nor the fully-qualified " +
301+
"name of a Java type on the classpath. Root cause: " + ex);
301302
}
302303
}
303304
}
@@ -306,6 +307,7 @@ protected Class<?> getDiscoveredReturningType() {
306307
return this.discoveredReturningType;
307308
}
308309

310+
@Nullable
309311
protected Type getDiscoveredReturningGenericType() {
310312
return this.discoveredReturningGenericType;
311313
}
@@ -330,8 +332,8 @@ protected void setThrowingNameNoCheck(String name) {
330332
}
331333
catch (Throwable ex) {
332334
throw new IllegalArgumentException("Throwing name '" + name +
333-
"' is neither a valid argument name nor the fully-qualified name of a Java type on the classpath. " +
334-
"Root cause: " + ex);
335+
"' is neither a valid argument name nor the fully-qualified " +
336+
"name of a Java type on the classpath. Root cause: " + ex);
335337
}
336338
}
337339
}
@@ -549,7 +551,9 @@ private void configurePointcutParameters(int argumentIndexOffset) {
549551
* @param ex the exception thrown by the method execution (may be null)
550552
* @return the empty array if there are no arguments
551553
*/
552-
protected Object[] argBinding(JoinPoint jp, JoinPointMatch jpMatch, @Nullable Object returnValue, @Nullable Throwable ex) {
554+
protected Object[] argBinding(JoinPoint jp, @Nullable JoinPointMatch jpMatch,
555+
@Nullable Object returnValue, @Nullable Throwable ex) {
556+
553557
calculateArgumentBindings();
554558

555559
// AMC start
@@ -608,13 +612,16 @@ else if (this.joinPointStaticPartArgumentIndex != -1) {
608612
* @return the invocation result
609613
* @throws Throwable in case of invocation failure
610614
*/
611-
protected Object invokeAdviceMethod(JoinPointMatch jpMatch, @Nullable Object returnValue, @Nullable Throwable ex) throws Throwable {
615+
protected Object invokeAdviceMethod(
616+
@Nullable JoinPointMatch jpMatch, @Nullable Object returnValue, @Nullable Throwable ex)
617+
throws Throwable {
618+
612619
return invokeAdviceMethodWithGivenArgs(argBinding(getJoinPoint(), jpMatch, returnValue, ex));
613620
}
614621

615622
// As above, but in this case we are given the join point.
616-
protected Object invokeAdviceMethod(JoinPoint jp, JoinPointMatch jpMatch, Object returnValue, Throwable t)
617-
throws Throwable {
623+
protected Object invokeAdviceMethod(JoinPoint jp, @Nullable JoinPointMatch jpMatch,
624+
@Nullable Object returnValue, @Nullable Throwable t) throws Throwable {
618625

619626
return invokeAdviceMethodWithGivenArgs(argBinding(jp, jpMatch, returnValue, t));
620627
}
@@ -649,6 +656,7 @@ protected JoinPoint getJoinPoint() {
649656
/**
650657
* Get the current join point match at the join point we are being dispatched on.
651658
*/
659+
@Nullable
652660
protected JoinPointMatch getJoinPointMatch() {
653661
MethodInvocation mi = ExposeInvocationInterceptor.currentInvocation();
654662
if (!(mi instanceof ProxyMethodInvocation)) {
@@ -663,8 +671,10 @@ protected JoinPointMatch getJoinPointMatch() {
663671
// 'last man wins' which is not what we want at all.
664672
// Using the expression is guaranteed to be safe, since 2 identical expressions
665673
// are guaranteed to bind in exactly the same way.
674+
@Nullable
666675
protected JoinPointMatch getJoinPointMatch(ProxyMethodInvocation pmi) {
667-
return (JoinPointMatch) pmi.getUserAttribute(this.pointcut.getExpression());
676+
String expression = this.pointcut.getExpression();
677+
return (expression != null ? (JoinPointMatch) pmi.getUserAttribute(expression) : null);
668678
}
669679

670680

spring-aop/src/main/java/org/springframework/aop/aspectj/AspectInstanceFactory.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2012 the original author or authors.
2+
* Copyright 2002-2017 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -17,6 +17,7 @@
1717
package org.springframework.aop.aspectj;
1818

1919
import org.springframework.core.Ordered;
20+
import org.springframework.lang.Nullable;
2021

2122
/**
2223
* Interface implemented to provide an instance of an AspectJ aspect.
@@ -40,8 +41,10 @@ public interface AspectInstanceFactory extends Ordered {
4041

4142
/**
4243
* Expose the aspect class loader that this factory uses.
43-
* @return the aspect class loader (never {@code null})
44+
* @return the aspect class loader (or {@code null} for the bootstrap loader)
45+
* @see org.springframework.util.ClassUtils#getDefaultClassLoader()
4446
*/
47+
@Nullable
4548
ClassLoader getAspectClassLoader();
4649

4750
}

spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAdviceParameterNameDiscoverer.java

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2016 the original author or authors.
2+
* Copyright 2002-2017 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -183,10 +183,11 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov
183183
* Create a new discoverer that attempts to discover parameter names
184184
* from the given pointcut expression.
185185
*/
186-
public AspectJAdviceParameterNameDiscoverer(String pointcutExpression) {
186+
public AspectJAdviceParameterNameDiscoverer(@Nullable String pointcutExpression) {
187187
this.pointcutExpression = pointcutExpression;
188188
}
189189

190+
190191
/**
191192
* Indicate whether {@link IllegalArgumentException} and {@link AmbiguousBindingException}
192193
* must be thrown as appropriate in the case of failing to deduce advice parameter names.
@@ -214,6 +215,7 @@ public void setThrowingName(String throwingName) {
214215
this.throwingName = throwingName;
215216
}
216217

218+
217219
/**
218220
* Deduce the parameter names for an advice method.
219221
* <p>See the {@link AspectJAdviceParameterNameDiscoverer class level javadoc}
@@ -475,8 +477,8 @@ else if (numAnnotationSlots == 1) {
475477
* If the token starts meets Java identifier conventions, it's in.
476478
*/
477479
@Nullable
478-
private String maybeExtractVariableName(String candidateToken) {
479-
if (candidateToken == null || candidateToken.equals("")) {
480+
private String maybeExtractVariableName(@Nullable String candidateToken) {
481+
if (!StringUtils.hasLength(candidateToken)) {
480482
return null;
481483
}
482484
if (Character.isJavaIdentifierStart(candidateToken.charAt(0)) &&
@@ -498,7 +500,7 @@ private String maybeExtractVariableName(String candidateToken) {
498500
* Given an args pointcut body (could be {@code args} or {@code at_args}),
499501
* add any candidate variable names to the given list.
500502
*/
501-
private void maybeExtractVariableNamesFromArgs(String argsSpec, List<String> varNames) {
503+
private void maybeExtractVariableNamesFromArgs(@Nullable String argsSpec, List<String> varNames) {
502504
if (argsSpec == null) {
503505
return;
504506
}
@@ -781,7 +783,7 @@ private static class PointcutBody {
781783

782784
private String text;
783785

784-
public PointcutBody(int tokens, String text) {
786+
public PointcutBody(int tokens, @Nullable String text) {
785787
this.numTokensConsumed = tokens;
786788
this.text = text;
787789
}

spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAroundAdvice.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2015 the original author or authors.
2+
* Copyright 2002-2017 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.

spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJExpressionPointcut.java

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2016 the original author or authors.
2+
* Copyright 2002-2017 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -57,6 +57,7 @@
5757
import org.springframework.beans.factory.annotation.BeanFactoryAnnotationUtils;
5858
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
5959
import org.springframework.lang.Nullable;
60+
import org.springframework.util.Assert;
6061
import org.springframework.util.ClassUtils;
6162
import org.springframework.util.ObjectUtils;
6263
import org.springframework.util.StringUtils;
@@ -196,6 +197,7 @@ private void checkReadyToMatch() {
196197
/**
197198
* Determine the ClassLoader to use for pointcut evaluation.
198199
*/
200+
@Nullable
199201
private ClassLoader determinePointcutClassLoader() {
200202
if (this.beanFactory instanceof ConfigurableBeanFactory) {
201203
return ((ConfigurableBeanFactory) this.beanFactory).getBeanClassLoader();
@@ -209,21 +211,27 @@ private ClassLoader determinePointcutClassLoader() {
209211
/**
210212
* Build the underlying AspectJ pointcut expression.
211213
*/
212-
private PointcutExpression buildPointcutExpression(ClassLoader classLoader) {
214+
private PointcutExpression buildPointcutExpression(@Nullable ClassLoader classLoader) {
213215
PointcutParser parser = initializePointcutParser(classLoader);
214216
PointcutParameter[] pointcutParameters = new PointcutParameter[this.pointcutParameterNames.length];
215217
for (int i = 0; i < pointcutParameters.length; i++) {
216218
pointcutParameters[i] = parser.createPointcutParameter(
217219
this.pointcutParameterNames[i], this.pointcutParameterTypes[i]);
218220
}
219-
return parser.parsePointcutExpression(replaceBooleanOperators(getExpression()),
221+
return parser.parsePointcutExpression(replaceBooleanOperators(resolveExpression()),
220222
this.pointcutDeclarationScope, pointcutParameters);
221223
}
222224

225+
private String resolveExpression() {
226+
String expression = getExpression();
227+
Assert.state(expression != null, "No expression set");
228+
return expression;
229+
}
230+
223231
/**
224232
* Initialize the underlying AspectJ pointcut parser.
225233
*/
226-
private PointcutParser initializePointcutParser(ClassLoader classLoader) {
234+
private PointcutParser initializePointcutParser(@Nullable ClassLoader classLoader) {
227235
PointcutParser parser = PointcutParser
228236
.getPointcutParserSupportingSpecifiedPrimitivesAndUsingSpecifiedClassLoaderForResolution(
229237
SUPPORTED_PRIMITIVES, classLoader);
@@ -301,7 +309,8 @@ else if (shadowMatch.neverMatches()) {
301309
// we say this is not a match as in Spring there will never be a different
302310
// runtime subtype.
303311
RuntimeTestWalker walker = getRuntimeTestWalker(shadowMatch);
304-
return (!walker.testsSubtypeSensitiveVars() || walker.testTargetInstanceOfResidue(targetClass));
312+
return (!walker.testsSubtypeSensitiveVars() ||
313+
(targetClass != null && walker.testTargetInstanceOfResidue(targetClass)));
305314
}
306315
}
307316

@@ -354,7 +363,7 @@ public boolean matches(Method method, @Nullable Class<?> targetClass, Object...
354363
* type but not 'this' (as would be the case of JDK dynamic proxies).
355364
* <p>See SPR-2979 for the original bug.
356365
*/
357-
if (pmi != null) { // there is a current invocation
366+
if (pmi != null && thisObject != null) { // there is a current invocation
358367
RuntimeTestWalker originalMethodResidueTest = getRuntimeTestWalker(originalShadowMatch);
359368
if (!originalMethodResidueTest.testThisInstanceOfResidue(thisObject.getClass())) {
360369
return false;
@@ -375,6 +384,7 @@ public boolean matches(Method method, @Nullable Class<?> targetClass, Object...
375384
}
376385
}
377386

387+
@Nullable
378388
protected String getCurrentProxiedBeanName() {
379389
return ProxyCreationContext.getCurrentProxiedBeanName();
380390
}
@@ -411,7 +421,7 @@ private void bindParameters(ProxyMethodInvocation invocation, JoinPointMatch jpm
411421
// 'last man wins' which is not what we want at all.
412422
// Using the expression is guaranteed to be safe, since 2 identical expressions
413423
// are guaranteed to bind in exactly the same way.
414-
invocation.setUserAttribute(getExpression(), jpm);
424+
invocation.setUserAttribute(resolveExpression(), jpm);
415425
}
416426

417427
private ShadowMatch getShadowMatch(Method targetMethod, Method originalMethod) {
@@ -600,7 +610,7 @@ public boolean mayNeedDynamicTest() {
600610
return false;
601611
}
602612

603-
private FuzzyBoolean contextMatch(Class<?> targetType) {
613+
private FuzzyBoolean contextMatch(@Nullable Class<?> targetType) {
604614
String advisedBeanName = getCurrentProxiedBeanName();
605615
if (advisedBeanName == null) { // no proxy creation in progress
606616
// abstain; can't return YES, since that will make pointcut with negation fail

spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJExpressionPointcutAdvisor.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2016 the original author or authors.
2+
* Copyright 2002-2017 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -20,6 +20,7 @@
2020
import org.springframework.aop.support.AbstractGenericPointcutAdvisor;
2121
import org.springframework.beans.factory.BeanFactory;
2222
import org.springframework.beans.factory.BeanFactoryAware;
23+
import org.springframework.lang.Nullable;
2324

2425
/**
2526
* Spring AOP Advisor that can be used for any AspectJ pointcut expression.
@@ -37,6 +38,7 @@ public void setExpression(String expression) {
3738
this.pointcut.setExpression(expression);
3839
}
3940

41+
@Nullable
4042
public String getExpression() {
4143
return this.pointcut.getExpression();
4244
}
@@ -45,6 +47,7 @@ public void setLocation(String location) {
4547
this.pointcut.setLocation(location);
4648
}
4749

50+
@Nullable
4851
public String getLocation() {
4952
return this.pointcut.getLocation();
5053
}

spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/AbstractAspectJAdvisorFactory.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2016 the original author or authors.
2+
* Copyright 2002-2017 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -212,6 +212,7 @@ private AspectJAnnotationType determineAnnotationType(A annotation) {
212212
throw new IllegalStateException("Unknown annotation type: " + annotation.toString());
213213
}
214214

215+
@Nullable
215216
private String resolveExpression(A annotation) throws Exception {
216217
String expression = null;
217218
for (String methodName : EXPRESSION_PROPERTIES) {

spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/AspectJProxyFactory.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2016 the original author or authors.
2+
* Copyright 2002-2017 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -119,7 +119,9 @@ public void addAspect(Class<?> aspectClass) {
119119
*/
120120
private void addAdvisorsFromAspectInstanceFactory(MetadataAwareAspectInstanceFactory instanceFactory) {
121121
List<Advisor> advisors = this.aspectFactory.getAdvisors(instanceFactory);
122-
advisors = AopUtils.findAdvisorsThatCanApply(advisors, getTargetClass());
122+
Class<?> targetClass = getTargetClass();
123+
Assert.state(targetClass != null, "Unresolvable target class");
124+
advisors = AopUtils.findAdvisorsThatCanApply(advisors, targetClass);
123125
AspectJProxyUtils.makeAdvisorChainAspectJCapableIfNecessary(advisors);
124126
AnnotationAwareOrderComparator.sort(advisors);
125127
addAdvisors(advisors);

0 commit comments

Comments
 (0)