Skip to content

Commit d3e09e9

Browse files
authored
Skip meta-annotated @Configuration in FinalClass and HideUtilityClassConstructor (#867)
Enables meta-annotation matching so classes annotated with `@TestConfiguration`, `@SpringBootApplication`, and any user-defined stereotype layered on top of `@Configuration` are also skipped.
1 parent ab8ff8d commit d3e09e9

4 files changed

Lines changed: 73 additions & 3 deletions

File tree

src/main/java/org/openrewrite/staticanalysis/FinalClassVisitor.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535

3636
public class FinalClassVisitor extends JavaIsoVisitor<ExecutionContext> {
3737

38-
private static final AnnotationMatcher CONFIGURATION_ANNOTATION = new AnnotationMatcher("@org.springframework.context.annotation.Configuration");
38+
private static final AnnotationMatcher CONFIGURATION_ANNOTATION = new AnnotationMatcher("@org.springframework.context.annotation.Configuration", true);
3939

4040
Tree visitRoot;
4141

@@ -75,7 +75,7 @@ public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDeclarat
7575
return cd;
7676
}
7777

78-
// Spring @Configuration classes are proxied at runtime and must not be final
78+
// Spring @Configuration classes (including meta-annotated ones like @TestConfiguration / @SpringBootApplication) are proxied at runtime and must not be final
7979
if (cd.getLeadingAnnotations().stream().anyMatch(a -> CONFIGURATION_ANNOTATION.matches(a))) {
8080
return cd;
8181
}

src/main/java/org/openrewrite/staticanalysis/HideUtilityClassConstructorVisitor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, P
168168
* Until then, however, we'll keep this private and unexposed.
169169
*/
170170
private final class UtilityClassMatcher {
171-
private final AnnotationMatcher configurationAnnotation = new AnnotationMatcher("@org.springframework.context.annotation.Configuration");
171+
private final AnnotationMatcher configurationAnnotation = new AnnotationMatcher("@org.springframework.context.annotation.Configuration", true);
172172
private final Collection<AnnotationMatcher> ignorableAnnotations;
173173

174174
private UtilityClassMatcher(Collection<String> ignorableAnnotations) {

src/test/java/org/openrewrite/staticanalysis/FinalClassTest.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,40 @@ private MyConfig() {}
215215
);
216216
}
217217

218+
@Issue("https://github.com/openrewrite/rewrite-static-analysis/issues/729")
219+
@Test
220+
void doNotFinalizeSpringTestConfigurationClass() {
221+
rewriteRun(
222+
//language=java
223+
java(
224+
"""
225+
package org.springframework.context.annotation;
226+
public @interface Configuration {}
227+
"""
228+
),
229+
//language=java
230+
java(
231+
"""
232+
package org.springframework.boot.test.context;
233+
import org.springframework.context.annotation.Configuration;
234+
@Configuration
235+
public @interface TestConfiguration {}
236+
"""
237+
),
238+
//language=java
239+
java(
240+
"""
241+
import org.springframework.boot.test.context.TestConfiguration;
242+
243+
@TestConfiguration
244+
class MyTestConfig {
245+
private MyTestConfig() {}
246+
}
247+
"""
248+
)
249+
);
250+
}
251+
218252
@Issue("https://github.com/openrewrite/rewrite-static-analysis/issues/372")
219253
@Test
220254
void doNotFinalizeClassWithNestedStaticFinalSubclass() {

src/test/java/org/openrewrite/staticanalysis/HideUtilityClassConstructorTest.java

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -566,6 +566,42 @@ public static String someBean() {
566566
);
567567
}
568568

569+
@Issue("https://github.com/openrewrite/rewrite-static-analysis/issues/729")
570+
@Test
571+
void doNotChangeSpringTestConfigurationClass() {
572+
rewriteRun(
573+
//language=java
574+
java(
575+
"""
576+
package org.springframework.context.annotation;
577+
public @interface Configuration {}
578+
"""
579+
),
580+
//language=java
581+
java(
582+
"""
583+
package org.springframework.boot.test.context;
584+
import org.springframework.context.annotation.Configuration;
585+
@Configuration
586+
public @interface TestConfiguration {}
587+
"""
588+
),
589+
//language=java
590+
java(
591+
"""
592+
import org.springframework.boot.test.context.TestConfiguration;
593+
594+
@TestConfiguration
595+
class MyTestConfig {
596+
public static String someBean() {
597+
return "bean";
598+
}
599+
}
600+
"""
601+
)
602+
);
603+
}
604+
569605
private static Consumer<RecipeSpec> hideUtilityClassConstructor(String... ignoreIfAnnotatedBy) {
570606
return spec -> spec.parser(JavaParser.fromJavaVersion().styles(
571607
singletonList(

0 commit comments

Comments
 (0)