From ad0cb299681b1426043c11c60c1b654796576491 Mon Sep 17 00:00:00 2001 From: Martin Lippert Date: Fri, 8 Nov 2024 15:51:21 +0100 Subject: [PATCH] GH-1397: find references to property keys from inside of annotations --- .../BootJavaLanguageServerComponents.java | 4 +- .../ide/vscode/boot/java/utils/ASTUtils.java | 5 +- .../boot/java/value/PropertyExtractor.java | 5 +- .../ValuePropertyReferencesProvider.java | 137 +++++++++++++++++- .../ValuePropertyReferenceFinderTest.java | 96 +++++++++++- .../src/main/java/application.properties | 3 +- .../PropertyUsageWithConditional.java | 10 ++ .../PropertyUsageWithConditionalAndArray.java | 10 ++ ...sageWithConditionalWithArrayAndPrefix.java | 10 ++ .../properties/PropertyUsageWithValue.java | 12 ++ 10 files changed, 277 insertions(+), 15 deletions(-) create mode 100644 headless-services/spring-boot-language-server/src/test/resources/test-projects/test-spring-indexing/src/main/java/org/test/properties/PropertyUsageWithConditional.java create mode 100644 headless-services/spring-boot-language-server/src/test/resources/test-projects/test-spring-indexing/src/main/java/org/test/properties/PropertyUsageWithConditionalAndArray.java create mode 100644 headless-services/spring-boot-language-server/src/test/resources/test-projects/test-spring-indexing/src/main/java/org/test/properties/PropertyUsageWithConditionalWithArrayAndPrefix.java create mode 100644 headless-services/spring-boot-language-server/src/test/resources/test-projects/test-spring-indexing/src/main/java/org/test/properties/PropertyUsageWithValue.java diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServerComponents.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServerComponents.java index 71fb542d2..d6425bd13 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServerComponents.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServerComponents.java @@ -318,8 +318,8 @@ public class BootJavaLanguageServerComponents implements LanguageServerComponent Map providers = new HashMap<>(); - providers.put(Annotations.VALUE, new ValuePropertyReferencesProvider(projectFinder)); - providers.put(Annotations.CONDITIONAL_ON_PROPERTY, new ValuePropertyReferencesProvider(projectFinder)); + providers.put(Annotations.VALUE, new ValuePropertyReferencesProvider(projectFinder, index)); + providers.put(Annotations.CONDITIONAL_ON_PROPERTY, new ValuePropertyReferencesProvider(projectFinder, index)); providers.put(Annotations.QUALIFIER, new QualifierReferencesProvider(index, symbolIndex)); providers.put(Annotations.NAMED_JAKARTA, new NamedReferencesProvider(index, symbolIndex)); providers.put(Annotations.NAMED_JAVAX, new NamedReferencesProvider(index, symbolIndex)); diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/ASTUtils.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/ASTUtils.java index 24217c3fd..104cc1b67 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/ASTUtils.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/ASTUtils.java @@ -449,7 +449,10 @@ public class ASTUtils { fieldAnnotations.add(annotation); String qualifiedName = annotation.resolveTypeBinding().getQualifiedName(); - if (Annotations.AUTOWIRED.equals(qualifiedName) || Annotations.INJECT_JAVAX.equals(qualifiedName) || Annotations.INJECT_JAKARTA.equals(qualifiedName)) { + if (Annotations.AUTOWIRED.equals(qualifiedName) + || Annotations.INJECT_JAVAX.equals(qualifiedName) + || Annotations.INJECT_JAKARTA.equals(qualifiedName) + || Annotations.VALUE.equals(qualifiedName)) { autowiredField = true; } } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/value/PropertyExtractor.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/value/PropertyExtractor.java index 4cd17518e..c5365b2cd 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/value/PropertyExtractor.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/value/PropertyExtractor.java @@ -15,16 +15,13 @@ import java.util.Map; import org.eclipse.jdt.core.dom.ASTNode; import org.eclipse.jdt.core.dom.Annotation; -import org.eclipse.jdt.core.dom.CompilationUnit; import org.eclipse.jdt.core.dom.Expression; import org.eclipse.jdt.core.dom.IAnnotationBinding; import org.eclipse.jdt.core.dom.MemberValuePair; import org.eclipse.jdt.core.dom.NormalAnnotation; import org.eclipse.jdt.core.dom.SingleMemberAnnotation; import org.eclipse.jdt.core.dom.StringLiteral; -import org.eclipse.lsp4j.LocationLink; import org.springframework.ide.vscode.boot.java.Annotations; -import org.springframework.ide.vscode.commons.java.IJavaProject; public class PropertyExtractor { @@ -103,7 +100,7 @@ public class PropertyExtractor { return null; } - private String extractPropertyKey(String s) { + public static String extractPropertyKey(String s) { if (s.length() > 3 && (s.startsWith("${") || s.startsWith("#{")) && s.endsWith("}")) { return s.substring(2, s.length() - 1); } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/value/ValuePropertyReferencesProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/value/ValuePropertyReferencesProvider.java index c6e787acd..8e88c4397 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/value/ValuePropertyReferencesProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/value/ValuePropertyReferencesProvider.java @@ -19,8 +19,11 @@ import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.function.BiFunction; import java.util.function.Function; @@ -38,11 +41,16 @@ import org.eclipse.lsp4j.Range; import org.eclipse.lsp4j.jsonrpc.CancelChecker; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.ide.vscode.boot.index.SpringMetamodelIndex; +import org.springframework.ide.vscode.boot.java.Annotations; import org.springframework.ide.vscode.boot.java.handlers.ReferenceProvider; import org.springframework.ide.vscode.boot.properties.BootPropertiesLanguageServerComponents; import org.springframework.ide.vscode.commons.java.IClasspathUtil; import org.springframework.ide.vscode.commons.java.IJavaProject; import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; +import org.springframework.ide.vscode.commons.protocol.spring.AnnotationMetadata; +import org.springframework.ide.vscode.commons.protocol.spring.Bean; +import org.springframework.ide.vscode.commons.protocol.spring.InjectionPoint; import org.springframework.ide.vscode.commons.util.text.TextDocument; import org.springframework.ide.vscode.commons.yaml.ast.YamlASTProvider; import org.springframework.ide.vscode.commons.yaml.ast.YamlFileAST; @@ -65,9 +73,12 @@ public class ValuePropertyReferencesProvider implements ReferenceProvider { private final JavaProjectFinder projectFinder; private final PropertyExtractor propertyExtractor; + private final SpringMetamodelIndex springIndex; - public ValuePropertyReferencesProvider(JavaProjectFinder projectFinder) { + + public ValuePropertyReferencesProvider(JavaProjectFinder projectFinder, SpringMetamodelIndex springIndex) { this.projectFinder = projectFinder; + this.springIndex = springIndex; this.propertyExtractor = new PropertyExtractor(); } @@ -80,7 +91,7 @@ public class ValuePropertyReferencesProvider implements ReferenceProvider { if (node instanceof StringLiteral) { String propertyKey = this.propertyExtractor.extractPropertyKey((StringLiteral) node); if (propertyKey != null) { - return findReferencesFromPropertyFiles(propertyKey); + return findReferencesToPropertyKey(propertyKey); } } } @@ -90,6 +101,126 @@ public class ValuePropertyReferencesProvider implements ReferenceProvider { return null; } + + public List findReferencesToPropertyKey(String propertyKey) { + List fromPropertyFiles = findReferencesFromPropertyFiles(propertyKey); + List fromAnnotations = findReferencesFromAnnotations(propertyKey); + + List result = new ArrayList<>(); + result.addAll(fromPropertyFiles); + result.addAll(fromAnnotations); + + return result; + } + + public List findReferencesFromAnnotations(String propertyKey) { + List result = new ArrayList<>(); + + Collection allProjects = this.projectFinder.all(); + for (IJavaProject project : allProjects) { + collectReferencesFromAnnotations(project, propertyKey, result); + } + + return result; + } + + private void collectReferencesFromAnnotations(IJavaProject project, String propertyKey, List result) { + Bean[] beans = springIndex.getBeansOfProject(project.getElementName()); + + if (beans != null) { + for (Bean bean : beans) { + collectReferencesFromAnnotations(bean, propertyKey, result); + } + } + } + + private void collectReferencesFromAnnotations(Bean bean, String propertyKey, List result) { + AnnotationMetadata[] annotations = bean.getAnnotations(); + for (AnnotationMetadata annotation : annotations) { + collectReferencesFromAnnotation(annotation, bean, propertyKey, result); + } + + InjectionPoint[] injectionPoints = bean.getInjectionPoints(); + for (InjectionPoint injectionPoint : injectionPoints) { + AnnotationMetadata[] annotationsFromInjectionPoint = injectionPoint.getAnnotations(); + for (AnnotationMetadata annotation : annotationsFromInjectionPoint) { + collectReferencesFromAnnotation(annotation, injectionPoint, propertyKey, result); + } + } + } + + private void collectReferencesFromAnnotation(AnnotationMetadata annotation, Bean bean, String propertyKey, List result) { + + if (Annotations.VALUE.equals(annotation.getAnnotationType())) { + Map attributes = annotation.getAttributes(); + String[] values = attributes.get("value"); + if (values != null && values.length > 0) { + for (String value : values) { + String extractedKey = PropertyExtractor.extractPropertyKey(value); + if (extractedKey != null && extractedKey.equals(propertyKey)) { + result.add(bean.getLocation()); + } + } + } + } + + else if (Annotations.CONDITIONAL_ON_PROPERTY.equals(annotation.getAnnotationType())) { + Map attributes = annotation.getAttributes(); + + String[] prefixes = attributes.get("prefix"); + String prefix = prefixes != null && prefixes.length == 1 ? prefixes[0] + "." : ""; + + String[] names = attributes.get("name"); + if (names == null) { + names = attributes.get("value"); + } + + if (names != null) { + for (String name : names) { + String key = prefix + name; + if (key.equals(propertyKey)) { + result.add(bean.getLocation()); + } + } + } + } + } + + private void collectReferencesFromAnnotation(AnnotationMetadata annotation, InjectionPoint injectionPoint, String propertyKey, List result) { + if (Annotations.VALUE.equals(annotation.getAnnotationType())) { + Map attributes = annotation.getAttributes(); + String[] values = attributes.get("value"); + if (values != null && values.length > 0) { + for (String value : values) { + String extractedKey = PropertyExtractor.extractPropertyKey(value); + if (extractedKey != null && extractedKey.equals(propertyKey)) { + result.add(injectionPoint.getLocation()); + } + } + } + } + + else if (Annotations.CONDITIONAL_ON_PROPERTY.equals(annotation.getAnnotationType())) { + Map attributes = annotation.getAttributes(); + + String[] prefixes = attributes.get("prefix"); + String prefix = prefixes != null && prefixes.length == 1 ? prefixes[0] + "." : ""; + + String[] names = attributes.get("name"); + if (names == null) { + names = attributes.get("value"); + } + + if (names != null) { + for (String name : names) { + String key = prefix + name; + if (key.equals(propertyKey)) { + result.add(injectionPoint.getLocation()); + } + } + } + } + } public List findReferencesFromPropertyFiles(String propertyKey) { Collection allProjects = this.projectFinder.all(); @@ -112,7 +243,7 @@ public class ValuePropertyReferencesProvider implements ReferenceProvider { .collect(Collectors.toList()); } catch (Exception e) { - return null; + return Collections.emptyList(); } } diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/value/test/ValuePropertyReferenceFinderTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/value/test/ValuePropertyReferenceFinderTest.java index 4f8c146c9..57fe8351d 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/value/test/ValuePropertyReferenceFinderTest.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/value/test/ValuePropertyReferenceFinderTest.java @@ -31,6 +31,7 @@ import org.springframework.context.annotation.Import; import org.springframework.ide.vscode.boot.app.SpringSymbolIndex; import org.springframework.ide.vscode.boot.bootiful.BootLanguageServerTest; import org.springframework.ide.vscode.boot.bootiful.SymbolProviderTestConf; +import org.springframework.ide.vscode.boot.index.SpringMetamodelIndex; import org.springframework.ide.vscode.boot.java.Annotations; import org.springframework.ide.vscode.boot.java.value.ValuePropertyReferencesProvider; import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; @@ -50,6 +51,7 @@ public class ValuePropertyReferenceFinderTest { @Autowired private BootLanguageServerHarness harness; @Autowired private JavaProjectFinder projectFinder; + @Autowired private SpringMetamodelIndex springIndex; @Autowired private SpringSymbolIndex indexer; private File directory; @@ -74,7 +76,7 @@ public class ValuePropertyReferenceFinderTest { @Test void testFindReferenceAtBeginningPropFile() throws Exception { - ValuePropertyReferencesProvider provider = new ValuePropertyReferencesProvider(projectFinder); + ValuePropertyReferencesProvider provider = new ValuePropertyReferencesProvider(projectFinder, springIndex); Path file = resourceDir.resolve("simple-case/application.properties"); List locations = provider.findReferences(file, "test.property"); @@ -93,7 +95,7 @@ public class ValuePropertyReferenceFinderTest { @Test void testFindReferenceAtBeginningYMLFile() throws Exception { - ValuePropertyReferencesProvider provider = new ValuePropertyReferencesProvider(projectFinder); + ValuePropertyReferencesProvider provider = new ValuePropertyReferencesProvider(projectFinder, springIndex); Path file = resourceDir.resolve("simple-yml/application.yml"); List locations = provider.findReferences(file, "test.property"); @@ -112,7 +114,7 @@ public class ValuePropertyReferenceFinderTest { @Test void testFindReferenceWithinMultipleFiles() throws Exception { - ValuePropertyReferencesProvider provider = new ValuePropertyReferencesProvider(projectFinder); + ValuePropertyReferencesProvider provider = new ValuePropertyReferencesProvider(projectFinder, springIndex); List locations = provider.findReferencesFromPropertyFiles("appl1.prop"); @@ -163,7 +165,7 @@ public class ValuePropertyReferenceFinderTest { } @Test - void testFindReferencesToPropertyFromValueAnnotation() throws Exception { + void testFindReferencesToPropertyFromValueAnnotationAtName() throws Exception { harness.getServer().getWorkspaceService().setWorkspaceFolders(List.of(new WorkspaceFolder(directory.toURI().toString()))); String completionLine = "@Value(\"${my.<*>prop}\")"; @@ -196,4 +198,90 @@ public class ValuePropertyReferenceFinderTest { assertEquals(0, location.getRange().getEnd().getLine()); assertEquals(7, location.getRange().getEnd().getCharacter()); } + + @Test + void testFindReferencesToPropertyFromValueAnnotationAtPrefix() throws Exception { + harness.getServer().getWorkspaceService().setWorkspaceFolders(List.of(new WorkspaceFolder(directory.toURI().toString()))); + + String completionLine = "@Value(\"${<*>my.prop}\")"; + + String editorContent = """ + package org.test; + + import + """ + + Annotations.VALUE + ";" + + """ + + @Component + """ + + completionLine + "\n" + + """ + public class TestDependsOnClass { + } + """; + + Editor editor = harness.newEditor(LanguageId.JAVA, editorContent, tempJavaDocUri); + List references = editor.getReferences(); + + assertEquals(1, references.size()); + + Location location = references.get(0); + assertEquals(directory.toPath().resolve("src/main/java/application.properties").toUri().toString(), location.getUri()); + assertEquals(0, location.getRange().getStart().getLine()); + assertEquals(0, location.getRange().getStart().getCharacter()); + assertEquals(0, location.getRange().getEnd().getLine()); + assertEquals(7, location.getRange().getEnd().getCharacter()); + } + + @Test + void testFindReferenceForPropertiesUsedInAnnotations() throws Exception { + ValuePropertyReferencesProvider provider = new ValuePropertyReferencesProvider(projectFinder, springIndex); + + List locations = provider.findReferencesToPropertyKey("my.prop2"); + + assertNotNull(locations); + assertEquals(5, locations.size()); + + URI propertiesFile = directory.toPath().resolve("src/main/java/application.properties").toUri(); + Location location = getLocation(locations, propertiesFile); + assertNotNull(location); + assertEquals(1, location.getRange().getStart().getLine()); + assertEquals(0, location.getRange().getStart().getCharacter()); + assertEquals(1, location.getRange().getEnd().getLine()); + assertEquals(8, location.getRange().getEnd().getCharacter()); + + URI javaFile = directory.toPath().resolve("src/main/java/org/test/properties/PropertyUsageWithValue.java").toUri(); + location = getLocation(locations, javaFile); + assertNotNull(location); + assertEquals(9, location.getRange().getStart().getLine()); + assertEquals(16, location.getRange().getStart().getCharacter()); + assertEquals(9, location.getRange().getEnd().getLine()); + assertEquals(24, location.getRange().getEnd().getCharacter()); + + javaFile = directory.toPath().resolve("src/main/java/org/test/properties/PropertyUsageWithConditional.java").toUri(); + location = getLocation(locations, javaFile); + assertNotNull(location); + assertEquals(5, location.getRange().getStart().getLine()); + assertEquals(0, location.getRange().getStart().getCharacter()); + assertEquals(5, location.getRange().getEnd().getLine()); + assertEquals(10, location.getRange().getEnd().getCharacter()); + + javaFile = directory.toPath().resolve("src/main/java/org/test/properties/PropertyUsageWithConditionalAndArray.java").toUri(); + location = getLocation(locations, javaFile); + assertNotNull(location); + assertEquals(5, location.getRange().getStart().getLine()); + assertEquals(0, location.getRange().getStart().getCharacter()); + assertEquals(5, location.getRange().getEnd().getLine()); + assertEquals(10, location.getRange().getEnd().getCharacter()); + + javaFile = directory.toPath().resolve("src/main/java/org/test/properties/PropertyUsageWithConditionalWithArrayAndPrefix.java").toUri(); + location = getLocation(locations, javaFile); + assertNotNull(location); + assertEquals(5, location.getRange().getStart().getLine()); + assertEquals(0, location.getRange().getStart().getCharacter()); + assertEquals(5, location.getRange().getEnd().getLine()); + assertEquals(10, location.getRange().getEnd().getCharacter()); +} + } diff --git a/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-spring-indexing/src/main/java/application.properties b/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-spring-indexing/src/main/java/application.properties index a638741e6..7ebf89b7e 100644 --- a/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-spring-indexing/src/main/java/application.properties +++ b/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-spring-indexing/src/main/java/application.properties @@ -1 +1,2 @@ -my.prop=test \ No newline at end of file +my.prop=test +my.prop2=test \ No newline at end of file diff --git a/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-spring-indexing/src/main/java/org/test/properties/PropertyUsageWithConditional.java b/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-spring-indexing/src/main/java/org/test/properties/PropertyUsageWithConditional.java new file mode 100644 index 000000000..6b5e3e643 --- /dev/null +++ b/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-spring-indexing/src/main/java/org/test/properties/PropertyUsageWithConditional.java @@ -0,0 +1,10 @@ +package org.test.properties; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.stereotype.Component; + +@Component +@ConditionalOnProperty("my.prop2") +public class PropertyUsageWithConditional { + +} \ No newline at end of file diff --git a/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-spring-indexing/src/main/java/org/test/properties/PropertyUsageWithConditionalAndArray.java b/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-spring-indexing/src/main/java/org/test/properties/PropertyUsageWithConditionalAndArray.java new file mode 100644 index 000000000..207260a99 --- /dev/null +++ b/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-spring-indexing/src/main/java/org/test/properties/PropertyUsageWithConditionalAndArray.java @@ -0,0 +1,10 @@ +package org.test.properties; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.stereotype.Component; + +@Component +@ConditionalOnProperty(name = {"my.prop2"}) +public class PropertyUsageWithConditionalAndArray { + +} \ No newline at end of file diff --git a/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-spring-indexing/src/main/java/org/test/properties/PropertyUsageWithConditionalWithArrayAndPrefix.java b/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-spring-indexing/src/main/java/org/test/properties/PropertyUsageWithConditionalWithArrayAndPrefix.java new file mode 100644 index 000000000..8aff70e79 --- /dev/null +++ b/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-spring-indexing/src/main/java/org/test/properties/PropertyUsageWithConditionalWithArrayAndPrefix.java @@ -0,0 +1,10 @@ +package org.test.properties; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.stereotype.Component; + +@Component +@ConditionalOnProperty(prefix = "my", name = {"prop2"}) +public class PropertyUsageWithConditionalWithArrayAndPrefix { + +} diff --git a/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-spring-indexing/src/main/java/org/test/properties/PropertyUsageWithValue.java b/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-spring-indexing/src/main/java/org/test/properties/PropertyUsageWithValue.java new file mode 100644 index 000000000..13e6e9e6b --- /dev/null +++ b/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-spring-indexing/src/main/java/org/test/properties/PropertyUsageWithValue.java @@ -0,0 +1,12 @@ +package org.test.properties; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +@Component +public class PropertyUsageWithValue { + + @Value("${my.prop2}") + private String someProp; + +} \ No newline at end of file