diff --git a/spring-modulith-core/src/main/java/org/springframework/modulith/core/ApplicationModule.java b/spring-modulith-core/src/main/java/org/springframework/modulith/core/ApplicationModule.java
index 0c62e7f30..42832974c 100644
--- a/spring-modulith-core/src/main/java/org/springframework/modulith/core/ApplicationModule.java
+++ b/spring-modulith-core/src/main/java/org/springframework/modulith/core/ApplicationModule.java
@@ -589,10 +589,12 @@ private static Classes filterSpringBeans(JavaPackage source) {
Classes repositories = source.that(isSpringDataRepository());
Classes coreComponents = source.that(not(INTERFACES).and(isComponent()));
Classes configurationProperties = source.that(isConfigurationProperties());
+ Classes jsr303Validator = source.that(isJsr303Validator());
return coreComponents //
.and(repositories) //
.and(configurationProperties) //
+ .and(jsr303Validator) //
.and(collect.getOrDefault(true, Collections.emptyList())) //
.and(collect.getOrDefault(false, Collections.emptyList()));
}
diff --git a/spring-modulith-core/src/main/java/org/springframework/modulith/core/Types.java b/spring-modulith-core/src/main/java/org/springframework/modulith/core/Types.java
index f6b18c93e..2b76e94dc 100644
--- a/spring-modulith-core/src/main/java/org/springframework/modulith/core/Types.java
+++ b/spring-modulith-core/src/main/java/org/springframework/modulith/core/Types.java
@@ -119,6 +119,10 @@ static DescribedPredicate super JavaClass> isConfigurationProperties() {
return isAnnotatedWith(AT_CONFIGURATION_PROPERTIES);
}
+ static DescribedPredicate super JavaClass> isJsr303Validator() {
+ return implement("jakarta.validation.ConstraintValidator");
+ }
+
static boolean isAtBeanMethod(JavaMethod method) {
return isAnnotatedWith(SpringTypes.AT_BEAN).test(method);
}
diff --git a/spring-modulith-integration-test/pom.xml b/spring-modulith-integration-test/pom.xml
index 7fbc37272..237ff3a68 100644
--- a/spring-modulith-integration-test/pom.xml
+++ b/spring-modulith-integration-test/pom.xml
@@ -52,6 +52,11 @@
spring-tx
+
+ jakarta.validation
+ jakarta.validation-api
+
+
diff --git a/spring-modulith-integration-test/src/main/java/com/acme/myproject/validator/SampleValidator.java b/spring-modulith-integration-test/src/main/java/com/acme/myproject/validator/SampleValidator.java
new file mode 100644
index 000000000..5eab68fe0
--- /dev/null
+++ b/spring-modulith-integration-test/src/main/java/com/acme/myproject/validator/SampleValidator.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2023 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.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.acme.myproject.validator;
+
+import jakarta.validation.ConstraintValidator;
+import jakarta.validation.ConstraintValidatorContext;
+
+import com.acme.myproject.moduleA.ServiceComponentA;
+
+public class SampleValidator implements ConstraintValidator {
+
+ public SampleValidator(ServiceComponentA dependency) {}
+
+ @Override
+ public boolean isValid(Object value, ConstraintValidatorContext context) {
+ return false;
+ }
+}
diff --git a/spring-modulith-integration-test/src/test/java/org/springframework/modulith/core/ApplicationModulesIntegrationTest.java b/spring-modulith-integration-test/src/test/java/org/springframework/modulith/core/ApplicationModulesIntegrationTest.java
index 7c64aa942..dafa1bc73 100644
--- a/spring-modulith-integration-test/src/test/java/org/springframework/modulith/core/ApplicationModulesIntegrationTest.java
+++ b/spring-modulith-integration-test/src/test/java/org/springframework/modulith/core/ApplicationModulesIntegrationTest.java
@@ -37,6 +37,7 @@
import com.acme.myproject.moduleA.ServiceComponentA;
import com.acme.myproject.moduleA.SomeConfigurationA.SomeAtBeanComponentA;
import com.acme.myproject.moduleB.ServiceComponentB;
+import com.acme.myproject.validator.SampleValidator;
/**
* @author Oliver Drotbohm
@@ -196,6 +197,20 @@ void excludesSpringAOTGeneratedTypes() {
});
}
+ @Test // GH-418
+ void detectsDependencyInducedByValidatorImplementation() {
+
+ assertThat(modules.getModuleByName("validator")).hasValueSatisfying(it -> {
+
+ assertThat(it.getSpringBeans())
+ .extracting(SpringBean::getFullyQualifiedTypeName)
+ .containsExactly(SampleValidator.class.getName());
+
+ assertThat(it.getBootstrapDependencies(modules).map(ApplicationModule::getName))
+ .containsExactly("moduleA");
+ });
+ }
+
private static void verifyNamedInterfaces(NamedInterfaces interfaces, String name, Class>... types) {
Stream.of(types).forEach(type -> {