diff --git a/java-frontend/src/main/java/org/sonar/java/model/springcontext/BeanDefinitionGatherer.java b/java-frontend/src/main/java/org/sonar/java/model/springcontext/BeanDefinitionGatherer.java index 3e8aca5bd99..1c9f79cfb94 100644 --- a/java-frontend/src/main/java/org/sonar/java/model/springcontext/BeanDefinitionGatherer.java +++ b/java-frontend/src/main/java/org/sonar/java/model/springcontext/BeanDefinitionGatherer.java @@ -306,6 +306,7 @@ private void collectBeanMethod(MethodTree method, String pkg) { private static Map> collectAutowiredDependencies(ClassTree classTree) { Map> deps = new LinkedHashMap<>(); + List unannotatedConstructors = new ArrayList<>(); for (Tree member : classTree.members()) { if (member instanceof VariableTree field) { if (field.symbol().metadata().isAnnotatedWith(SpringUtils.AUTOWIRED_ANNOTATION)) { @@ -313,14 +314,19 @@ private static Map> collectAutowiredDependencies(ClassTree c String name = dependencyKey(field.simpleName().name(), extractQualifier(field.symbol().metadata())); deps.computeIfAbsent(typeFqn, k -> new LinkedHashSet<>()).add(name); } - } else if (member.is(Tree.Kind.CONSTRUCTOR, Tree.Kind.METHOD)) { - MethodTree method = (MethodTree) member; + } else if (member instanceof MethodTree method) { if (method.symbol().metadata().isAnnotatedWith(SpringUtils.AUTOWIRED_ANNOTATION)) { parameterDependencies(method).forEach((type, names) -> deps.computeIfAbsent(type, k -> new LinkedHashSet<>()).addAll(names)); + } else if (method.is(Tree.Kind.CONSTRUCTOR)) { + unannotatedConstructors.add(method); } } } + if (deps.isEmpty() && unannotatedConstructors.size() == 1) { + parameterDependencies(unannotatedConstructors.get(0)).forEach((type, names) -> + deps.computeIfAbsent(type, k -> new LinkedHashSet<>()).addAll(names)); + } return deps; } diff --git a/java-frontend/src/test/files/springcontext/MultipleConstructorsNoDependencies.java b/java-frontend/src/test/files/springcontext/MultipleConstructorsNoDependencies.java new file mode 100644 index 00000000000..9efb0906c9f --- /dev/null +++ b/java-frontend/src/test/files/springcontext/MultipleConstructorsNoDependencies.java @@ -0,0 +1,22 @@ +package checks.spring.context; + +import org.springframework.context.ApplicationContext; +import org.springframework.core.env.Environment; +import org.springframework.stereotype.Component; + +@Component +class MultipleConstructorsNoDependencies { + + private final ApplicationContext applicationContext; + private final Environment environment; + + MultipleConstructorsNoDependencies(ApplicationContext applicationContext) { + this.applicationContext = applicationContext; + this.environment = null; + } + + MultipleConstructorsNoDependencies(ApplicationContext applicationContext, Environment environment) { + this.applicationContext = applicationContext; + this.environment = environment; + } +} diff --git a/java-frontend/src/test/files/springcontext/SingleConstructorDependencies.java b/java-frontend/src/test/files/springcontext/SingleConstructorDependencies.java new file mode 100644 index 00000000000..a48f1cb31c5 --- /dev/null +++ b/java-frontend/src/test/files/springcontext/SingleConstructorDependencies.java @@ -0,0 +1,17 @@ +package checks.spring.context; + +import org.springframework.context.ApplicationContext; +import org.springframework.core.env.Environment; +import org.springframework.stereotype.Component; + +@Component +class SingleConstructorDependencies { + + private final ApplicationContext applicationContext; + private final Environment environment; + + SingleConstructorDependencies(ApplicationContext applicationContext, Environment environment) { + this.applicationContext = applicationContext; + this.environment = environment; + } +} diff --git a/java-frontend/src/test/java/org/sonar/java/model/springcontext/BeanDefinitionGathererTest.java b/java-frontend/src/test/java/org/sonar/java/model/springcontext/BeanDefinitionGathererTest.java index ff99b05f174..d4ac03cedbc 100644 --- a/java-frontend/src/test/java/org/sonar/java/model/springcontext/BeanDefinitionGathererTest.java +++ b/java-frontend/src/test/java/org/sonar/java/model/springcontext/BeanDefinitionGathererTest.java @@ -201,10 +201,22 @@ static Stream dependencyCollectionArguments() { return Stream.of( Arguments.of("src/test/files/springcontext/AutowiredDependencies.java", "autowiredDependencies"), Arguments.of("src/test/files/springcontext/AutowiredConstructorDependencies.java", "autowiredConstructorDependencies"), - Arguments.of("src/test/files/springcontext/BeanMethodWithDependencies.java", "myBean") + Arguments.of("src/test/files/springcontext/BeanMethodWithDependencies.java", "myBean"), + Arguments.of("src/test/files/springcontext/SingleConstructorDependencies.java", "singleConstructorDependencies") ); } + // ---- Implicit single-constructor injection -------------------------------- + + @Test + void multiple_constructors_without_autowired_yields_no_dependencies() { + scan("src/test/files/springcontext/MultipleConstructorsNoDependencies.java"); + + var beans = model.getBeanDefinitionRegistry().getByName("multipleConstructorsNoDependencies"); + assertThat(beans).hasSize(1); + assertThat(beans.get(0).getDependingBeans()).isEmpty(); + } + // ---- @Qualifier handling -------------------------------------------------- @ParameterizedTest(name = "{0}")