Skip to content

Commit d510b73

Browse files
committed
Match if empty by default in InstanceFilter and ExceptionTypeFilter
Prior to this commit, the constructors for InstanceFilter and ExceptionTypeFilter required one to supply the matchIfEmpty flag. However, users will typically want that to be true. Moreover, we always supply true for the matchIfEmpty flag within the Spring Framework. This commit therefore makes the matchIfEmpty flag optional by introducing overloaded constructors for InstanceFilter and ExceptionTypeFilter that only accept the includes and excludes collections. In addition, this commit overhauls the Javadoc for InstanceFilter and ExceptionTypeFilter, fixing several issues in the documentation. Furthermore, this commit applies consistent @⁠Nullable declarations in ExceptionTypeFilter. Closes gh-35158
1 parent 46c40e7 commit d510b73

File tree

7 files changed

+97
-39
lines changed

7 files changed

+97
-39
lines changed

spring-context-support/src/main/java/org/springframework/cache/jcache/interceptor/AbstractJCacheOperation.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ public CacheInvocationParameter[] getAllParameters(@Nullable Object... values) {
134134
protected ExceptionTypeFilter createExceptionTypeFilter(
135135
Class<? extends Throwable>[] includes, Class<? extends Throwable>[] excludes) {
136136

137-
return new ExceptionTypeFilter(Arrays.asList(includes), Arrays.asList(excludes), true);
137+
return new ExceptionTypeFilter(Arrays.asList(includes), Arrays.asList(excludes));
138138
}
139139

140140

spring-context/src/main/java/org/springframework/resilience/retry/MethodRetrySpec.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ public MethodRetrySpec(MethodRetryPredicate predicate, long maxAttempts, Duratio
6464

6565

6666
MethodRetryPredicate combinedPredicate() {
67-
ExceptionTypeFilter exceptionFilter = new ExceptionTypeFilter(this.includes, this.excludes, true);
67+
ExceptionTypeFilter exceptionFilter = new ExceptionTypeFilter(this.includes, this.excludes);
6868
return (method, throwable) -> exceptionFilter.match(throwable.getClass()) &&
6969
this.predicate.shouldRetry(method, throwable);
7070
}

spring-core/src/main/java/org/springframework/core/retry/DefaultRetryPolicy.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ class DefaultRetryPolicy implements RetryPolicy {
5151

5252
this.includes = includes;
5353
this.excludes = excludes;
54-
this.exceptionFilter = new ExceptionTypeFilter(this.includes, this.excludes, true);
54+
this.exceptionFilter = new ExceptionTypeFilter(this.includes, this.excludes);
5555
this.predicate = predicate;
5656
this.backOff = backOff;
5757
}

spring-core/src/main/java/org/springframework/util/ExceptionTypeFilter.java

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,21 +18,63 @@
1818

1919
import java.util.Collection;
2020

21+
import org.jspecify.annotations.Nullable;
22+
2123
/**
22-
* An {@link InstanceFilter} implementation that handles exception types. A type
23-
* will match against a given candidate if it is assignable to that candidate.
24+
* An {@link InstanceFilter} that handles exception types.
25+
*
26+
* <p>An exception type will match against a given candidate if it is assignable
27+
* to that candidate.
2428
*
2529
* @author Stephane Nicoll
30+
* @author Sam Brannen
2631
* @since 4.1
2732
*/
2833
public class ExceptionTypeFilter extends InstanceFilter<Class<? extends Throwable>> {
2934

30-
public ExceptionTypeFilter(Collection<? extends Class<? extends Throwable>> includes,
31-
Collection<? extends Class<? extends Throwable>> excludes, boolean matchIfEmpty) {
35+
/**
36+
* Create a new {@code ExceptionTypeFilter} based on include and exclude
37+
* collections, with the {@code matchIfEmpty} flag set to {@code true}.
38+
* <p>See {@link #ExceptionTypeFilter(Collection, Collection, boolean)} for
39+
* details.
40+
* @param includes the collection of includes
41+
* @param excludes the collection of excludes
42+
* @since 7.0
43+
*/
44+
public ExceptionTypeFilter(@Nullable Collection<Class<? extends Throwable>> includes,
45+
@Nullable Collection<Class<? extends Throwable>> excludes) {
46+
47+
super(includes, excludes);
48+
}
49+
50+
/**
51+
* Create a new {@code ExceptionTypeFilter} based on include and exclude
52+
* collections.
53+
* <p>See {@link InstanceFilter#InstanceFilter(Collection, Collection, boolean)
54+
* InstanceFilter} for details.
55+
* @param includes the collection of includes
56+
* @param excludes the collection of excludes
57+
* @param matchIfEmpty the matching result if the includes and the excludes
58+
* collections are both {@code null} or empty
59+
*/
60+
public ExceptionTypeFilter(@Nullable Collection<? extends Class<? extends Throwable>> includes,
61+
@Nullable Collection<? extends Class<? extends Throwable>> excludes, boolean matchIfEmpty) {
3262

3363
super(includes, excludes, matchIfEmpty);
3464
}
3565

66+
67+
/**
68+
* Determine if the specified {@code instance} matches the specified
69+
* {@code candidate}.
70+
* <p>By default, the two instances match if the {@code candidate} type is
71+
* {@linkplain Class#isAssignableFrom(Class) is assignable from} the
72+
* {@code instance} type.
73+
* <p>Can be overridden by subclasses.
74+
* @param instance the instance to check
75+
* @param candidate a candidate defined by this filter
76+
* @return {@code true} if the instance matches the candidate
77+
*/
3678
@Override
3779
protected boolean match(Class<? extends Throwable> instance, Class<? extends Throwable> candidate) {
3880
return candidate.isAssignableFrom(instance);

spring-core/src/main/java/org/springframework/util/InstanceFilter.java

Lines changed: 39 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,14 @@
2222
import org.jspecify.annotations.Nullable;
2323

2424
/**
25-
* A simple instance filter that checks if a given instance match based on
26-
* a collection of includes and excludes element.
25+
* A simple instance filter that checks if a given instance matches based on
26+
* collections of includes and excludes.
2727
*
28-
* <p>Subclasses may want to override {@link #match(Object, Object)} to provide
29-
* a custom matching algorithm.
28+
* <p>Subclasses may override {@link #match(Object, Object)} to provide a custom
29+
* matching algorithm.
3030
*
3131
* @author Stephane Nicoll
32+
* @author Sam Brannen
3233
* @since 4.1
3334
* @param <T> the instance type
3435
*/
@@ -42,17 +43,33 @@ public class InstanceFilter<T> {
4243

4344

4445
/**
45-
* Create a new instance based on includes/excludes collections.
46-
* <p>A particular element will match if it "matches" the one of the element in the
47-
* includes list and does not match one of the element in the excludes list.
48-
* <p>Subclasses may redefine what matching means. By default, an element match with
49-
* another if it is equals according to {@link Object#equals(Object)}
50-
* <p>If both collections are empty, {@code matchIfEmpty} defines if
51-
* an element matches or not.
46+
* Create a new {@code InstanceFilter} based on include and exclude collections,
47+
* with the {@code matchIfEmpty} flag set to {@code true}.
48+
* <p>See {@link #InstanceFilter(Collection, Collection, boolean)} for details.
5249
* @param includes the collection of includes
5350
* @param excludes the collection of excludes
54-
* @param matchIfEmpty the matching result if both the includes and the excludes
55-
* collections are empty
51+
* @since 7.0
52+
*/
53+
public InstanceFilter(@Nullable Collection<? extends T> includes,
54+
@Nullable Collection<? extends T> excludes) {
55+
56+
this(includes, excludes, true);
57+
}
58+
59+
/**
60+
* Create a new {@code InstanceFilter} based on include and exclude collections.
61+
* <p>A particular element will match if it <em>matches</em> one of the elements
62+
* in the {@code includes} list and does not match one of the elements in the
63+
* {@code excludes} list.
64+
* <p>Subclasses may redefine what matching means. By default, an element
65+
* {@linkplain #match(Object, Object) matches} another if the two elements are
66+
* {@linkplain Object#equals(Object) equal}.
67+
* <p>If both collections are empty, {@code matchIfEmpty} defines if an element
68+
* matches or not.
69+
* @param includes the collection of includes
70+
* @param excludes the collection of excludes
71+
* @param matchIfEmpty the matching result if the includes and the excludes
72+
* collections are both {@code null} or empty
5673
*/
5774
public InstanceFilter(@Nullable Collection<? extends T> includes,
5875
@Nullable Collection<? extends T> excludes, boolean matchIfEmpty) {
@@ -87,9 +104,12 @@ public boolean match(T instance) {
87104
}
88105

89106
/**
90-
* Determine if the specified {@code instance} is equal to the
91-
* specified {@code candidate}.
92-
* @param instance the instance to handle
107+
* Determine if the specified {@code instance} matches the specified
108+
* {@code candidate}.
109+
* <p>By default, the two instances match if they are
110+
* {@linkplain Object#equals(Object) equal}.
111+
* <p>Can be overridden by subclasses.
112+
* @param instance the instance to check
93113
* @param candidate a candidate defined by this filter
94114
* @return {@code true} if the instance matches the candidate
95115
*/
@@ -99,10 +119,10 @@ protected boolean match(T instance, T candidate) {
99119

100120
/**
101121
* Determine if the specified {@code instance} matches one of the candidates.
102-
* <p>If the candidates collection is {@code null}, returns {@code false}.
103122
* @param instance the instance to check
104-
* @param candidates a list of candidates
105-
* @return {@code true} if the instance match or the candidates collection is null
123+
* @param candidates the collection of candidates
124+
* @return {@code true} if the instance matches; {@code false} if the
125+
* candidates collection is empty or there is no match
106126
*/
107127
protected boolean match(T instance, Collection<? extends T> candidates) {
108128
for (T candidate : candidates) {

spring-core/src/test/java/org/springframework/util/ExceptionTypeFilterTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ class ExceptionTypeFilterTests {
2929

3030
@Test
3131
void subClassMatch() {
32-
ExceptionTypeFilter filter = new ExceptionTypeFilter(List.of(RuntimeException.class), null, true);
32+
ExceptionTypeFilter filter = new ExceptionTypeFilter(List.of(RuntimeException.class), null);
3333
assertThat(filter.match(RuntimeException.class)).isTrue();
3434
assertThat(filter.match(IllegalStateException.class)).isTrue();
3535
}

spring-core/src/test/java/org/springframework/util/InstanceFilterTests.java

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020

2121
import org.junit.jupiter.api.Test;
2222

23-
import static java.util.Arrays.asList;
2423
import static org.assertj.core.api.Assertions.assertThat;
2524

2625
/**
@@ -30,47 +29,44 @@ class InstanceFilterTests {
3029

3130
@Test
3231
void emptyFilterApplyMatchIfEmpty() {
33-
InstanceFilter<String> filter = new InstanceFilter<>(null, null, true);
32+
InstanceFilter<String> filter = new InstanceFilter<>(null, null);
3433
match(filter, "foo");
3534
match(filter, "bar");
3635
}
3736

3837
@Test
3938
void includesFilter() {
40-
InstanceFilter<String> filter = new InstanceFilter<>(
41-
asList("First", "Second"), null, true);
39+
InstanceFilter<String> filter = new InstanceFilter<>(List.of("First", "Second"), null);
4240
match(filter, "Second");
4341
doNotMatch(filter, "foo");
4442
}
4543

4644
@Test
4745
void excludesFilter() {
48-
InstanceFilter<String> filter = new InstanceFilter<>(
49-
null, asList("First", "Second"), true);
46+
InstanceFilter<String> filter = new InstanceFilter<>(null, List.of("First", "Second"));
5047
doNotMatch(filter, "Second");
5148
match(filter, "foo");
5249
}
5350

5451
@Test
5552
void includesAndExcludesFilters() {
56-
InstanceFilter<String> filter = new InstanceFilter<>(
57-
asList("foo", "Bar"), asList("First", "Second"), true);
53+
InstanceFilter<String> filter = new InstanceFilter<>(List.of("foo", "Bar"), List.of("First", "Second"));
5854
doNotMatch(filter, "Second");
5955
match(filter, "foo");
6056
}
6157

6258
@Test
6359
void includesAndExcludesFiltersConflict() {
64-
InstanceFilter<String> filter = new InstanceFilter<>(
65-
List.of("First"), List.of("First"), true);
60+
InstanceFilter<String> filter = new InstanceFilter<>(List.of("First"), List.of("First"));
6661
doNotMatch(filter, "First");
6762
}
6863

69-
private <T> void match(InstanceFilter<T> filter, T candidate) {
64+
65+
private static <T> void match(InstanceFilter<T> filter, T candidate) {
7066
assertThat(filter.match(candidate)).as("filter '" + filter + "' should match " + candidate).isTrue();
7167
}
7268

73-
private <T> void doNotMatch(InstanceFilter<T> filter, T candidate) {
69+
private static <T> void doNotMatch(InstanceFilter<T> filter, T candidate) {
7470
assertThat(filter.match(candidate)).as("filter '" + filter + "' should not match " + candidate).isFalse();
7571
}
7672

0 commit comments

Comments
 (0)