Skip to content

Commit 7dd3258

Browse files
authored
Reduce ServiceLoader Invocations (#210)
* start * fix tests * rename loader * Update ExtensionLoader.java
1 parent d78c194 commit 7dd3258

File tree

29 files changed

+197
-79
lines changed

29 files changed

+197
-79
lines changed

blackbox-test/src/main/java/module-info.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@
55
requires io.avaje.validation.contraints;
66
requires jakarta.validation;
77
requires jakarta.inject;
8-
provides io.avaje.validation.Validator.GeneratedComponent with example.avaje.valid.GeneratedValidatorComponent;
8+
provides io.avaje.validation.spi.ValidationExtension with example.avaje.valid.GeneratedValidatorComponent;
99
provides io.avaje.inject.spi.InjectExtension with example.avaje.GeneratedModule;
1010
}

pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
<maven.compiler.release>17</maven.compiler.release>
3030
<inject.version>10.0-RC7</inject.version>
3131
<http.version>2.0-RC2</http.version>
32+
<spi.version>1.9</spi.version>
3233
</properties>
3334

3435
<modules>

validator-generator/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
<dependency>
3737
<groupId>io.avaje</groupId>
3838
<artifactId>avaje-spi-service</artifactId>
39-
<version>1.9</version>
39+
<version>${spi.version}</version>
4040
</dependency>
4141
<dependency>
4242
<groupId>io.avaje</groupId>

validator-generator/src/main/java/io/avaje/validation/generator/Constants.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
final class Constants {
66

77
static final String META_INF_COMPONENT =
8-
"META-INF/services/io.avaje.validation.Validator$GeneratedComponent";
8+
"META-INF/services/io.avaje.validation.spi.ValidationExtension";
99
static final String META_INF_CUSTOMIZER =
1010
"META-INF/services/io.avaje.validation.spi.ValidatorCustomizer";
1111
public static final String VALID_SPI = "io.avaje.validation.spi.*";

validator-generator/src/main/java/io/avaje/validation/generator/SimpleComponentWriter.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ private void writeMetaDataEntry(List<String> entries) {
142142
private void writeImports() {
143143
importTypes.add(Constants.VALIDATOR);
144144
importTypes.add(Constants.VALID_SPI);
145-
importTypes.add("io.avaje.validation.Validator.GeneratedComponent");
145+
importTypes.add("io.avaje.validation.spi.GeneratedComponent");
146146
importTypes.addAll(metaData.allImports());
147147

148148
for (final String importType : importTypes) {

validator-http-plugin/pom.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,13 @@
2020
<optional>true</optional>
2121
</dependency>
2222

23+
<dependency>
24+
<groupId>io.avaje</groupId>
25+
<artifactId>avaje-spi-service</artifactId>
26+
<version>${spi.version}</version>
27+
<optional>true</optional>
28+
</dependency>
29+
2330
<dependency>
2431
<groupId>io.avaje</groupId>
2532
<artifactId>avaje-validator-inject-plugin</artifactId>

validator-http-plugin/src/main/java/io/avaje/validation/http/HttpValidatorProvider.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package io.avaje.validation.http;
22

33
import io.avaje.inject.BeanScopeBuilder;
4+
import io.avaje.spi.ServiceProvider;
45

56
import java.util.ArrayList;
67
import java.util.Arrays;
@@ -9,7 +10,8 @@
910
/**
1011
* Plugin for avaje inject that provides a default Http Validator instance.
1112
*/
12-
public final class HttpValidatorProvider implements io.avaje.inject.spi.Plugin {
13+
@ServiceProvider
14+
public final class HttpValidatorProvider implements io.avaje.inject.spi.InjectPlugin {
1315

1416
private static final Class<?> VALIDATOR_HTTP_CLASS = avajeHttpOnClasspath();
1517

@@ -33,7 +35,7 @@ public void apply(BeanScopeBuilder builder) {
3335
}
3436

3537
builder.provideDefault(null, VALIDATOR_HTTP_CLASS, () -> {
36-
final var props = builder.propertyPlugin();
38+
final var props = builder.configPlugin();
3739
final var locales = new ArrayList<Locale>();
3840

3941
props.get("validation.locale.default")

validator-http-plugin/src/main/java/module-info.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
requires transitive io.avaje.validation.plugin;
66
requires transitive io.avaje.http.api;
7+
requires static io.avaje.spi;
78

8-
provides io.avaje.inject.spi.Plugin with io.avaje.validation.http.HttpValidatorProvider;
9+
provides io.avaje.inject.spi.InjectExtension with io.avaje.validation.http.HttpValidatorProvider;
910
}

validator-http-plugin/src/main/resources/META-INF/services/io.avaje.inject.spi.Plugin

Lines changed: 0 additions & 1 deletion
This file was deleted.

validator-inject-plugin/pom.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,13 @@
3535
<scope>test</scope>
3636
</dependency>
3737

38+
<dependency>
39+
<groupId>io.avaje</groupId>
40+
<artifactId>avaje-spi-service</artifactId>
41+
<version>${spi.version}</version>
42+
<optional>true</optional>
43+
</dependency>
44+
3845
<dependency>
3946
<groupId>io.avaje</groupId>
4047
<artifactId>junit</artifactId>

validator-inject-plugin/src/main/java/io/avaje/validation/inject/spi/DefaultValidatorProvider.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,16 @@
1010
import io.avaje.inject.BeanScopeBuilder;
1111
import io.avaje.inject.aop.AspectProvider;
1212
import io.avaje.inject.spi.GenericType;
13+
import io.avaje.inject.spi.InjectPlugin;
14+
import io.avaje.spi.ServiceProvider;
1315
import io.avaje.validation.ValidMethod;
1416
import io.avaje.validation.Validator;
1517
import io.avaje.validation.adapter.MethodAdapterProvider;
1618
import io.avaje.validation.inject.aspect.AOPMethodValidator;
1719

18-
/** Plugin for avaje inject that provides a default Jsonb instance. */
19-
public final class DefaultValidatorProvider implements io.avaje.inject.spi.Plugin {
20+
/** Plugin for avaje inject that provides a default Validator instance. */
21+
@ServiceProvider
22+
public final class DefaultValidatorProvider implements InjectPlugin {
2023

2124
@Override
2225
public Class<?>[] provides() {
@@ -39,7 +42,7 @@ private void validator(BeanScopeBuilder builder) {
3942
null,
4043
Validator.class,
4144
() -> {
42-
final var props = builder.propertyPlugin();
45+
final var props = builder.configPlugin();
4346
final var validator =
4447
Validator.builder().failFast(props.equalTo("validation.failFast", "true"));
4548

validator-inject-plugin/src/main/java/module-info.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
requires transitive io.avaje.validation;
44
requires transitive io.avaje.inject;
55
requires transitive io.avaje.inject.aop;
6+
requires static io.avaje.spi;
67

7-
provides io.avaje.inject.spi.Plugin with io.avaje.validation.inject.spi.DefaultValidatorProvider;
8+
provides io.avaje.inject.spi.InjectExtension with io.avaje.validation.inject.spi.DefaultValidatorProvider;
89
}

validator-inject-plugin/src/main/resources/META-INF/services/io.avaje.inject.spi.Plugin

Lines changed: 0 additions & 1 deletion
This file was deleted.

validator/pom.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,13 @@
4646
<optional>true</optional>
4747
</dependency>
4848

49+
<dependency>
50+
<groupId>io.avaje</groupId>
51+
<artifactId>avaje-spi-service</artifactId>
52+
<version>${spi.version}</version>
53+
<optional>true</optional>
54+
</dependency>
55+
4956
<dependency>
5057
<groupId>jakarta.validation</groupId>
5158
<artifactId>jakarta.validation-api</artifactId>

validator/src/main/java/io/avaje/validation/Validator.java

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
import io.avaje.validation.adapter.ValidationContext;
1515
import io.avaje.validation.adapter.ValidationContext.AdapterCreateRequest;
1616
import io.avaje.validation.core.DefaultBootstrap;
17+
import io.avaje.validation.spi.AdapterFactory;
18+
import io.avaje.validation.spi.AnnotationFactory;
1719
import io.avaje.validation.spi.MessageInterpolator;
1820
import io.avaje.validation.spi.ValidatorCustomizer;
1921

@@ -160,10 +162,10 @@ interface Builder {
160162
Builder add(ValidatorCustomizer component);
161163

162164
/** Add a ValidationAdapter.Factory which provides ValidationAdapters to use. */
163-
Builder add(ValidationContext.AdapterFactory factory);
165+
Builder add(AdapterFactory factory);
164166

165167
/** Add a ValidationAdapter.Factory which provides ValidationAdapters to use. */
166-
Builder add(ValidationContext.AnnotationFactory factory);
168+
Builder add(AnnotationFactory factory);
167169

168170
/**
169171
* Build and return the Validator instance with all the given adapters and factories registered.
@@ -187,13 +189,4 @@ interface AnnotationAdapterBuilder {
187189
/** Create a ValidationAdapter given the Validator instance. */
188190
ValidationAdapter<?> build(AdapterCreateRequest request);
189191
}
190-
191-
/** Components register ValidationAdapters with the Validator.Builder */
192-
@FunctionalInterface
193-
interface GeneratedComponent extends ValidatorCustomizer {
194-
195-
/** Customize the Builder with generated ValidationAdapters. */
196-
@Override
197-
void customize(Builder builder);
198-
}
199192
}

validator/src/main/java/io/avaje/validation/adapter/ValidationContext.java

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -116,36 +116,6 @@ interface Message {
116116
*/
117117
String lookupkey();
118118
}
119-
120-
/** Factory for creating a ValidationAdapter for a given type. */
121-
@FunctionalInterface
122-
interface AdapterFactory {
123-
124-
/**
125-
* Create and return a ValidationAdapter given the type and annotations or return null.
126-
* Returning null means that the adapter could be created by another factory.
127-
*
128-
* @param type The type for which the adapter is being created
129-
* @param ctx The validation context
130-
* @return The created validation adapter or null if not applicable
131-
*/
132-
ValidationAdapter<?> create(Type type, ValidationContext ctx);
133-
}
134-
135-
/** Factory for creating an Annotation Adapter for a given annotation. */
136-
@FunctionalInterface
137-
interface AnnotationFactory {
138-
139-
/**
140-
* Create and return a ValidationAdapter given the type and annotations or return null.
141-
* Returning null means that the adapter could be created by another factory.
142-
*
143-
* @param request Holds the details used to create the adapter
144-
* @return The created validation adapter or null if not applicable
145-
*/
146-
ValidationAdapter<?> create(AdapterCreateRequest request);
147-
}
148-
149119
/** Request to create a Validation Adapter. */
150120
interface AdapterCreateRequest {
151121

validator/src/main/java/io/avaje/validation/core/CoreAdapterBuilder.java

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@
44
import java.lang.reflect.Type;
55
import java.time.Clock;
66
import java.time.Duration;
7-
import java.util.*;
7+
import java.util.ArrayList;
8+
import java.util.HashMap;
9+
import java.util.List;
10+
import java.util.Map;
11+
import java.util.Set;
812
import java.util.concurrent.ConcurrentHashMap;
913
import java.util.function.Supplier;
1014

@@ -15,6 +19,8 @@
1519
import io.avaje.validation.core.adapters.FuturePastAdapterFactory;
1620
import io.avaje.validation.core.adapters.NumberAdapters;
1721
import io.avaje.validation.groups.Default;
22+
import io.avaje.validation.spi.AdapterFactory;
23+
import io.avaje.validation.spi.AnnotationFactory;
1824

1925
/** Builds and caches the ValidationAdapter adapters for DValidator. */
2026
final class CoreAdapterBuilder {
@@ -23,14 +29,14 @@ final class CoreAdapterBuilder {
2329

2430
private static final Set<Class<?>> DEFAULT_GROUP = Set.of(Default.class);
2531
private final DValidator context;
26-
private final List<ValidationContext.AdapterFactory> factories = new ArrayList<>();
27-
private final List<ValidationContext.AnnotationFactory> annotationFactories = new ArrayList<>();
32+
private final List<AdapterFactory> factories = new ArrayList<>();
33+
private final List<AnnotationFactory> annotationFactories = new ArrayList<>();
2834
private final Map<Object, ValidationAdapter<?>> adapterCache = new ConcurrentHashMap<>();
2935

3036
CoreAdapterBuilder(
3137
DValidator context,
32-
List<ValidationContext.AdapterFactory> userFactories,
33-
List<ValidationContext.AnnotationFactory> userAnnotationFactories,
38+
List<AdapterFactory> userFactories,
39+
List<AnnotationFactory> userAnnotationFactories,
3440
Supplier<Clock> clockSupplier,
3541
Duration temporalTolerance) {
3642
this.context = context;
@@ -62,7 +68,7 @@ <T> ValidationAdapter<T> annotationAdapter(
6268
@SuppressWarnings("unchecked")
6369
<T> ValidationAdapter<T> build(Type type, Object cacheKey) {
6470
// Ask each factory to create the validation adapter.
65-
for (final ValidationContext.AdapterFactory factory : factories) {
71+
for (final AdapterFactory factory : factories) {
6672
final var result = (ValidationAdapter<T>) factory.create(type, context);
6773
if (result != null) {
6874
return result;

validator/src/main/java/io/avaje/validation/core/DValidator.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
import java.util.Map;
1818
import java.util.Optional;
1919
import java.util.ResourceBundle;
20-
import java.util.ServiceLoader;
2120
import java.util.Set;
2221
import java.util.concurrent.ConcurrentHashMap;
2322
import java.util.function.Supplier;
@@ -28,12 +27,16 @@
2827
import io.avaje.validation.adapter.ValidationAdapter;
2928
import io.avaje.validation.adapter.ValidationContext;
3029
import io.avaje.validation.adapter.ValidationRequest;
30+
import io.avaje.validation.spi.AdapterFactory;
31+
import io.avaje.validation.spi.AnnotationFactory;
32+
import io.avaje.validation.spi.GeneratedComponent;
3133
import io.avaje.validation.spi.MessageInterpolator;
3234
import io.avaje.validation.spi.ValidatorCustomizer;
3335

3436
/** Default implementation of Validator. */
3537
final class DValidator implements Validator, ValidationContext {
3638

39+
private static final ExtensionLoader SPI_LOADER = new ExtensionLoader();
3740
private final CoreAdapterBuilder builder;
3841
private final Map<Type, ValidationType<?>> typeCache = new ConcurrentHashMap<>();
3942
private final MessageInterpolator interpolator;
@@ -283,10 +286,10 @@ public Builder messageInterpolator(MessageInterpolator interpolator) {
283286

284287
private void registerComponents() {
285288
// first register all user defined ValidatorCustomizer
286-
for (final ValidatorCustomizer next : ServiceLoader.load(ValidatorCustomizer.class)) {
289+
for (final ValidatorCustomizer next : SPI_LOADER.customizers()) {
287290
next.customize(this);
288291
}
289-
for (final GeneratedComponent next : ServiceLoader.load(GeneratedComponent.class)) {
292+
for (final GeneratedComponent next : SPI_LOADER.generatedComponents()) {
290293
next.customize(this);
291294
}
292295
}
@@ -298,7 +301,7 @@ public DValidator build() {
298301
final var localeResolver = new LocaleResolver(defaultLocale, otherLocales);
299302
final var interpolator =
300303
Optional.ofNullable(this.userInterpolator)
301-
.or(() -> ServiceLoader.load(MessageInterpolator.class).findFirst())
304+
.or(SPI_LOADER::interpolator)
302305
.orElseGet(BasicMessageInterpolator::new);
303306

304307
return new DValidator(
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package io.avaje.validation.core;
2+
3+
import java.util.ArrayList;
4+
import java.util.List;
5+
import java.util.Optional;
6+
import java.util.ServiceLoader;
7+
8+
import io.avaje.validation.spi.AdapterFactory;
9+
import io.avaje.validation.spi.AnnotationFactory;
10+
import io.avaje.validation.spi.GeneratedComponent;
11+
import io.avaje.validation.spi.MessageInterpolator;
12+
import io.avaje.validation.spi.ValidatorCustomizer;
13+
import io.avaje.validation.spi.ValidationExtension;
14+
15+
/** Load all the services using the common service interface. */
16+
final class ExtensionLoader {
17+
18+
private final List<GeneratedComponent> generatedComponents = new ArrayList<>();
19+
private final List<ValidatorCustomizer> customizers = new ArrayList<>();
20+
private final List<AdapterFactory> adapterFactories = new ArrayList<>();
21+
private final List<AnnotationFactory> annotationFactories = new ArrayList<>();
22+
private Optional<MessageInterpolator> interpolator = Optional.empty();
23+
24+
ExtensionLoader() {
25+
for (var spi : ServiceLoader.load(ValidationExtension.class)) {
26+
if (spi instanceof GeneratedComponent gc) {
27+
generatedComponents.add(gc);
28+
} else if (spi instanceof ValidatorCustomizer c) {
29+
customizers.add(c);
30+
} else if (spi instanceof MessageInterpolator m) {
31+
interpolator = Optional.of(m);
32+
} else if (spi instanceof AdapterFactory af) {
33+
adapterFactories.add(af);
34+
} else if (spi instanceof AnnotationFactory af) {
35+
annotationFactories.add(af);
36+
}
37+
}
38+
}
39+
40+
Optional<MessageInterpolator> interpolator() {
41+
return interpolator;
42+
}
43+
44+
List<GeneratedComponent> generatedComponents() {
45+
return generatedComponents;
46+
}
47+
48+
List<ValidatorCustomizer> customizers() {
49+
return customizers;
50+
}
51+
52+
List<AdapterFactory> adapterFactories() {
53+
return adapterFactories;
54+
}
55+
56+
List<AnnotationFactory> annotationFactories() {
57+
return annotationFactories;
58+
}
59+
}

0 commit comments

Comments
 (0)