From 53cc57fbd94074b19e60ba77d62d08b8b287c235 Mon Sep 17 00:00:00 2001 From: Martin Lippert Date: Mon, 31 Mar 2025 12:16:29 +0200 Subject: [PATCH] GH-1529: factories indexer now creates bean index elements as well --- .../vscode/boot/app/SpringSymbolIndex.java | 5 +- .../java/utils/SpringFactoriesIndexer.java | 254 +++++++++++------- .../SpringMetamodelIndexerFactoriesTest.java | 92 +++++++ ...egisteredBeansAdvancedReconcilingTest.java | 1 - .../resources/META-INF/spring/aot.factories | 2 + 5 files changed, 262 insertions(+), 92 deletions(-) create mode 100644 headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/index/test/SpringMetamodelIndexerFactoriesTest.java create mode 100644 headless-services/spring-boot-language-server/src/test/resources/test-projects/test-spring-indexing/src/main/resources/META-INF/spring/aot.factories diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/SpringSymbolIndex.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/SpringSymbolIndex.java index 227934df0..e41319b4f 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/SpringSymbolIndex.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/SpringSymbolIndex.java @@ -251,7 +251,10 @@ public class SpringSymbolIndex implements InitializingBean, SpringIndex { springIndexerJava = new SpringIndexerJava(handler, specificProviders, this.cache, projectFinder(), server.getProgressService(), jdtReconciler, problemCollectorFactory, config.getJavaValidationSettingsJson(), cuCache); factoriesIndexer = new SpringFactoriesIndexer(handler, cache); - this.indexers = new SpringIndexer[] {springIndexerJava, factoriesIndexer}; + this.indexers = new SpringIndexer[] { + factoriesIndexer, // factories indexder should be the first, so that java indexers and reconcilers can rely on those indexed elements + springIndexerJava + }; getWorkspaceService().onDidChangeWorkspaceFolders(evt -> { log.debug("workspace roots have changed event arrived - added: " + evt.getEvent().getAdded() + " - removed: " + evt.getEvent().getRemoved()); diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringFactoriesIndexer.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringFactoriesIndexer.java index b6bda41a9..2bfd188c4 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringFactoriesIndexer.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringFactoriesIndexer.java @@ -23,6 +23,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -41,9 +42,13 @@ import org.springframework.ide.vscode.boot.index.cache.IndexCache; import org.springframework.ide.vscode.boot.index.cache.IndexCacheKey; import org.springframework.ide.vscode.boot.java.beans.BeanUtils; import org.springframework.ide.vscode.boot.java.beans.BeansIndexer; +import org.springframework.ide.vscode.boot.java.beans.CachedBean; import org.springframework.ide.vscode.commons.java.IClasspathUtil; import org.springframework.ide.vscode.commons.java.IJavaProject; import org.springframework.ide.vscode.commons.protocol.java.Classpath; +import org.springframework.ide.vscode.commons.protocol.spring.Bean; +import org.springframework.ide.vscode.commons.protocol.spring.DefaultValues; +import org.springframework.ide.vscode.commons.protocol.spring.SpringIndexElement; import org.springframework.ide.vscode.commons.util.text.LanguageId; import org.springframework.ide.vscode.commons.util.text.Region; import org.springframework.ide.vscode.commons.util.text.TextDocument; @@ -61,6 +66,9 @@ public class SpringFactoriesIndexer implements SpringIndexer { // we need to change the generation - this will result in a re-indexing due to no up-to-date cache data being found private static final String GENERATION = "GEN-12"; + private static final String SYMBOL_KEY = "symbols"; + private static final String BEANS_KEY = "beans"; + private static final String FILE_PATTERN = "**/META-INF/spring/*.factories"; private static final PathMatcher FILE_GLOB_PATTERN = FileSystems.getDefault().getPathMatcher("glob:" + FILE_PATTERN); @@ -99,12 +107,12 @@ public class SpringFactoriesIndexer implements SpringIndexer { @Override public List computeSymbols(IJavaProject project, String docURI, String content) throws Exception { - return computeSymbols(docURI, content); + return computeSymbols(docURI, content).symbols; } @Override public List computeDocumentSymbols(IJavaProject project, String docURI, String content) throws Exception { - return computeSymbols(docURI, content).stream() + return computeSymbols(docURI, content).symbols.stream() .map(workspaceSymbol -> convertToDocumentSymbol(workspaceSymbol)) .toList(); } @@ -124,8 +132,10 @@ public class SpringFactoriesIndexer implements SpringIndexer { return new DocumentSymbol(workspaceSymbol.getName(), workspaceSymbol.getKind(), range, range); } - private List computeSymbols(String docURI, String content) { + private ComputeResult computeSymbols(String docURI, String content) { ImmutableList.Builder symbols = ImmutableList.builder(); + ImmutableList.Builder beans = ImmutableList.builder(); + PropertiesAst ast = new AntlrParser().parse(content).ast; if (ast != null) { for (KeyValuePair pair : ast.getPropertyValuePairs()) { @@ -141,10 +151,18 @@ public class SpringFactoriesIndexer implements SpringIndexer { String simpleName = getSimpleName(fqName); String beanId = BeanUtils.getBeanNameFromType(simpleName); Range range = doc.toRange(new Region(pair.getOffset(), pair.getLength())); - symbols.add(new WorkspaceSymbol( + Location location = new Location(docURI, range); + + WorkspaceSymbol symbol = new WorkspaceSymbol( BeansIndexer.beanLabel(false, beanId, fqName, Paths.get(URI.create(docURI)).getFileName().toString()), SymbolKind.Interface, - Either.forLeft(new Location(docURI, range)))); + Either.forLeft(location)); + + symbols.add(symbol); + + Bean bean = new Bean(beanId, fqName, location, DefaultValues.EMPTY_INJECTION_POINTS, Collections.emptySet(), DefaultValues.EMPTY_ANNOTATIONS, false, symbol.getName()); + beans.add(bean); + } catch (Exception e) { log.error("", e); } @@ -152,7 +170,7 @@ public class SpringFactoriesIndexer implements SpringIndexer { } } } - return symbols.build(); + return new ComputeResult(symbols.build(), beans.build()); } private static String getSimpleName(String fqName) { @@ -163,22 +181,6 @@ public class SpringFactoriesIndexer implements SpringIndexer { return fqName; } - private IndexCacheKey getCacheKey(IJavaProject project) { - String filesIndentifier = getFiles(project).stream() - .filter(f -> Files.isRegularFile(f)) - .map(f -> { - try { - return f.toAbsolutePath().toString() + "#" + Files.getLastModifiedTime(f).toMillis(); - } catch (IOException e) { - log.error("", e); - return f.toAbsolutePath().toString() + "#0"; - } - }) - .collect(Collectors.joining(",")); - return new IndexCacheKey(project.getElementName(), "factories", "", DigestUtils.md5Hex(GENERATION + "-" + filesIndentifier).toUpperCase()); - } - - @Override public void initializeProject(IJavaProject project, boolean clean) throws Exception { long startTime = System.currentTimeMillis(); @@ -187,27 +189,39 @@ public class SpringFactoriesIndexer implements SpringIndexer { log.info("scan factories files for symbols for project: " + project.getElementName() + " - no. of files: " + files.size()); - IndexCacheKey cacheKey = getCacheKey(project); + IndexCacheKey symbolsCacheKey = getCacheKey(project, SYMBOL_KEY); + IndexCacheKey beansCacheKey = getCacheKey(project, BEANS_KEY); - CachedSymbol[] symbols = this.cache.retrieveSymbols(cacheKey, filesStr, CachedSymbol.class); - if (symbols == null || clean) { + CachedSymbol[] symbols = this.cache.retrieveSymbols(symbolsCacheKey, filesStr, CachedSymbol.class); + CachedBean[] beans = this.cache.retrieveSymbols(beansCacheKey, filesStr, CachedBean.class); + + if (symbols == null || beans == null || clean) { List generatedSymbols = new ArrayList(); + List generatedBeans = new ArrayList(); for (Path file : files) { - generatedSymbols.addAll(scanFile(file)); + ScanResult scanResult = scanFile(file); + + if (scanResult != null) { + generatedSymbols.addAll(scanResult.symbols); + generatedBeans.addAll(scanResult.beans); + } } - this.cache.store(cacheKey, filesStr, generatedSymbols, null, CachedSymbol.class); + this.cache.store(symbolsCacheKey, filesStr, generatedSymbols, null, CachedSymbol.class); + this.cache.store(beansCacheKey, filesStr, generatedBeans, null, CachedBean.class); symbols = (CachedSymbol[]) generatedSymbols.toArray(new CachedSymbol[generatedSymbols.size()]); + beans = (CachedBean[]) generatedBeans.toArray(new CachedBean[generatedBeans.size()]); } else { log.info("scan factories files used cached data: " + project.getElementName() + " - no. of cached symbols retrieved: " + symbols.length); } - if (symbols != null) { + if (symbols != null && beans != null) { WorkspaceSymbol[] enhancedSymbols = Arrays.stream(symbols).map(cachedSymbol -> cachedSymbol.getEnhancedSymbol()).toArray(WorkspaceSymbol[]::new); - symbolHandler.addSymbols(project, enhancedSymbols, null, null); + Map> beansByDoc = Arrays.stream(beans).filter(cachedBean -> cachedBean.getBean() != null).collect(Collectors.groupingBy(CachedBean::getDocURI, Collectors.mapping(CachedBean::getBean, Collectors.toList()))); + symbolHandler.addSymbols(project, enhancedSymbols, beansByDoc, null); } long endTime = System.currentTimeMillis(); @@ -216,19 +230,117 @@ public class SpringFactoriesIndexer implements SpringIndexer { } - private List scanFile(Path file) { + @Override + public void removeProject(IJavaProject project) throws Exception { + IndexCacheKey symbolsCacheKey = getCacheKey(project, SYMBOL_KEY); + IndexCacheKey beansCacheKey = getCacheKey(project, BEANS_KEY); + + this.cache.remove(symbolsCacheKey); + this.cache.remove(beansCacheKey); + } + + @Override + public void updateFile(IJavaProject project, DocumentDescriptor updatedDoc, String content) throws Exception { + this.symbolHandler.removeSymbols(project, updatedDoc.getDocURI()); + + List outputFolders = IClasspathUtil.getOutputFolders(project.getClasspath()).map(f -> f.toPath()).collect(Collectors.toList()); + String docURI = updatedDoc.getDocURI(); + Path path = Paths.get(URI.create(docURI)); + + if (!outputFolders.stream().anyMatch(out -> path.startsWith(out))) { + + ScanResult scanResult = scanFile(path); + + List generatedSymbols = Collections.emptyList(); + List generatedBeans = Collections.emptyList(); + + if (scanResult != null) { + generatedSymbols = scanResult.symbols; + generatedBeans = scanResult.beans; + } + + IndexCacheKey symbolsCacheKey = getCacheKey(project, SYMBOL_KEY); + IndexCacheKey beansCacheKey = getCacheKey(project, BEANS_KEY); + + String file = new File(new URI(docURI)).getAbsolutePath(); + this.cache.update(symbolsCacheKey, file, updatedDoc.getLastModified(), generatedSymbols, null, CachedSymbol.class); + this.cache.update(beansCacheKey, file, updatedDoc.getLastModified(), generatedBeans, null, CachedBean.class); + + WorkspaceSymbol[] symbols = generatedSymbols.stream().map(cachedSymbol -> cachedSymbol.getEnhancedSymbol()).toArray(WorkspaceSymbol[]::new); + List beans = generatedBeans.stream().filter(cachedBean -> cachedBean.getBean() != null).map(cachedBean -> cachedBean.getBean()).toList(); + symbolHandler.addSymbols(project, docURI, symbols, beans, null); + } + } + + @Override + public void updateFiles(IJavaProject project, DocumentDescriptor[] updatedDocs) throws Exception { + IndexCacheKey symbolsCacheKey = getCacheKey(project, SYMBOL_KEY); + IndexCacheKey beansCacheKey = getCacheKey(project, BEANS_KEY); + + List outputFolders = IClasspathUtil.getOutputFolders(project.getClasspath()).map(f -> f.toPath()).collect(Collectors.toList()); + + for (DocumentDescriptor d : updatedDocs) { + Path path = Paths.get(URI.create(d.getDocURI())); + + if (!outputFolders.stream().anyMatch(out -> path.startsWith(out))) { + + if (Files.isRegularFile(path)) { + + updateFile(project, d, Files.readString(path)); + + } else { + String file = new File(new URI(d.getDocURI())).getAbsolutePath(); + + cache.removeFile(symbolsCacheKey, file, CachedSymbol.class); + cache.removeFile(beansCacheKey, file, CachedBean.class); + } + } + } + } + + @Override + public void removeFiles(IJavaProject project, String[] docURIs) throws Exception { + String[] files = Arrays.stream(docURIs).map(docURI -> { + try { + return new File(new URI(docURI)).getAbsolutePath(); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + }).toArray(String[]::new); + + IndexCacheKey symbolsCacheKey = getCacheKey(project, SYMBOL_KEY); + IndexCacheKey beansCacheKey = getCacheKey(project, SYMBOL_KEY); + + cache.removeFiles(symbolsCacheKey, files, CachedSymbol.class); + cache.removeFiles(beansCacheKey, files, CachedBean.class); + } + + private ScanResult scanFile(Path file) { try { + String content = Files.readString(file); - ImmutableList.Builder builder = ImmutableList.builder(); + + ImmutableList.Builder symbols = ImmutableList.builder(); + ImmutableList.Builder beans = ImmutableList.builder(); + long lastModified = Files.getLastModifiedTime(file).toMillis(); String docUri = file.toUri().toASCIIString(); - for (WorkspaceSymbol s : computeSymbols(docUri, content)) { - builder.add(new CachedSymbol(docUri, lastModified, s)); + + ComputeResult result = computeSymbols(docUri, content); + + for (WorkspaceSymbol symbol : result.symbols) { + symbols.add(new CachedSymbol(docUri, lastModified, symbol)); } - return builder.build(); + + for (Bean bean : result.beans) { + beans.add(new CachedBean(docUri, bean)); + } + + return new ScanResult(symbols.build(), beans.build()); + } catch (IOException e) { log.error("", e); - return Collections.emptyList(); + return null; } } @@ -256,60 +368,22 @@ public class SpringFactoriesIndexer implements SpringIndexer { } - @Override - public void removeProject(IJavaProject project) throws Exception { - IndexCacheKey cacheKey = getCacheKey(project); - this.cache.remove(cacheKey); + private IndexCacheKey getCacheKey(IJavaProject project, String elementType) { + String filesIndentifier = getFiles(project).stream() + .filter(f -> Files.isRegularFile(f)) + .map(f -> { + try { + return f.toAbsolutePath().toString() + "#" + Files.getLastModifiedTime(f).toMillis(); + } catch (IOException e) { + log.error("", e); + return f.toAbsolutePath().toString() + "#0"; + } + }) + .collect(Collectors.joining(",")); + return new IndexCacheKey(project.getElementName(), "factories", elementType, DigestUtils.md5Hex(GENERATION + "-" + filesIndentifier).toUpperCase()); } - - @Override - public void updateFile(IJavaProject project, DocumentDescriptor updatedDoc, String content) throws Exception { - this.symbolHandler.removeSymbols(project, updatedDoc.getDocURI()); - - List outputFolders = IClasspathUtil.getOutputFolders(project.getClasspath()).map(f -> f.toPath()).collect(Collectors.toList()); - String docURI = updatedDoc.getDocURI(); - Path path = Paths.get(URI.create(docURI)); - if (!outputFolders.stream().anyMatch(out -> path.startsWith(out))) { - List generatedSymbols = scanFile(path); - - IndexCacheKey cacheKey = getCacheKey(project); - String file = new File(new URI(docURI)).getAbsolutePath(); - this.cache.update(cacheKey, file, updatedDoc.getLastModified(), generatedSymbols, null, CachedSymbol.class); - - WorkspaceSymbol[] symbols = generatedSymbols.stream().map(cachedSymbol -> cachedSymbol.getEnhancedSymbol()).toArray(WorkspaceSymbol[]::new); - symbolHandler.addSymbols(project, docURI, symbols, null, null); - } - } - - @Override - public void updateFiles(IJavaProject project, DocumentDescriptor[] updatedDocs) throws Exception { - IndexCacheKey key = getCacheKey(project); - List outputFolders = IClasspathUtil.getOutputFolders(project.getClasspath()).map(f -> f.toPath()).collect(Collectors.toList()); - for (DocumentDescriptor d : updatedDocs) { - Path path = Paths.get(URI.create(d.getDocURI())); - if (!outputFolders.stream().anyMatch(out -> path.startsWith(out))) { - if (Files.isRegularFile(path)) { - updateFile(project, d, Files.readString(path)); - } else { - String file = new File(new URI(d.getDocURI())).getAbsolutePath(); - cache.removeFile(key, file, CachedSymbol.class); - } - } - } - } - - @Override - public void removeFiles(IJavaProject project, String[] docURIs) throws Exception { - String[] files = Arrays.stream(docURIs).map(docURI -> { - try { - return new File(new URI(docURI)).getAbsolutePath(); - } catch (URISyntaxException e) { - throw new RuntimeException(e); - } - }).toArray(String[]::new); - - IndexCacheKey key = getCacheKey(project); - cache.removeFiles(key, files, CachedSymbol.class); - } - + + private static record ScanResult (List symbols, List beans) {} + private static record ComputeResult (List symbols, List beans) {} + } diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/index/test/SpringMetamodelIndexerFactoriesTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/index/test/SpringMetamodelIndexerFactoriesTest.java new file mode 100644 index 000000000..24f0c2675 --- /dev/null +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/index/test/SpringMetamodelIndexerFactoriesTest.java @@ -0,0 +1,92 @@ +/******************************************************************************* + * Copyright (c) 2025 Broadcom + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Broadcom - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.boot.index.test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; + +import java.io.File; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; + +import org.eclipse.lsp4j.TextDocumentIdentifier; +import org.eclipse.lsp4j.WorkspaceSymbol; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +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.commons.languageserver.java.JavaProjectFinder; +import org.springframework.ide.vscode.commons.protocol.spring.Bean; +import org.springframework.ide.vscode.project.harness.BootLanguageServerHarness; +import org.springframework.ide.vscode.project.harness.ProjectsHarness; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +/** + * @author Martin Lippert + */ +@ExtendWith(SpringExtension.class) +@BootLanguageServerTest +@Import(SymbolProviderTestConf.class) +public class SpringMetamodelIndexerFactoriesTest { + + @Autowired private BootLanguageServerHarness harness; + @Autowired private JavaProjectFinder projectFinder; + @Autowired private SpringMetamodelIndex springIndex; + @Autowired private SpringSymbolIndex indexer; + + private File directory; + + @BeforeEach + public void setup() throws Exception { + harness.intialize(null); + + directory = new File(ProjectsHarness.class.getResource("/test-projects/test-spring-indexing/").toURI()); + + String projectDir = directory.toURI().toString(); + + // trigger project creation + projectFinder.find(new TextDocumentIdentifier(projectDir)).get(); + + CompletableFuture initProject = indexer.waitOperation(); + initProject.get(5, TimeUnit.SECONDS); + } + + @Test + void testSymbolForAotProcessorFromFactoriesFile() { + String docUri = directory.toPath().resolve("src/main/resources/META-INF/spring/aot.factories").toUri().toString(); + + List symbols = indexer.getWorkspaceSymbolsFromSymbolIndex(docUri); + + assertEquals(1, symbols.size()); + WorkspaceSymbol symbol = symbols.get(0); + + assertEquals("@+ 'registeredViaFactoriesBeanRegistrationAotProcessor' (aot.factories) org.test.aot.RegisteredViaFactoriesBeanRegistrationAotProcessor", symbol.getName()); + assertEquals(docUri, symbol.getLocation().getLeft().getUri()); + } + + @Test + void testIndexElementsForAotProcessorFromFactoriesFile() { + Bean[] beans = springIndex.getBeansWithName("test-spring-indexing", "registeredViaFactoriesBeanRegistrationAotProcessor"); + + assertEquals(1, beans.length); + assertEquals("registeredViaFactoriesBeanRegistrationAotProcessor", beans[0].getName()); + assertEquals("org.test.aot.RegisteredViaFactoriesBeanRegistrationAotProcessor", beans[0].getType()); + + assertFalse(beans[0].isConfiguration()); + } + +} diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/reconcilers/test/NotRegisteredBeansAdvancedReconcilingTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/reconcilers/test/NotRegisteredBeansAdvancedReconcilingTest.java index 80ef4817a..b976e80d4 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/reconcilers/test/NotRegisteredBeansAdvancedReconcilingTest.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/reconcilers/test/NotRegisteredBeansAdvancedReconcilingTest.java @@ -118,7 +118,6 @@ public class NotRegisteredBeansAdvancedReconcilingTest { } @Test - @Disabled // this case needs work - (1) factories not taken into account at all, (2) requires complete index doesn't take other indexers into account yet void testBasicValidationOfAotProcessorRegisteredViaFactoriesFile() throws Exception { String docUri = directory.toPath().resolve("src/main/java/org/test/aot/RegisteredViaFactoriesBeanRegistrationAotProcessor.java").toUri().toString(); diff --git a/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-spring-indexing/src/main/resources/META-INF/spring/aot.factories b/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-spring-indexing/src/main/resources/META-INF/spring/aot.factories new file mode 100644 index 000000000..a9226d4f8 --- /dev/null +++ b/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-spring-indexing/src/main/resources/META-INF/spring/aot.factories @@ -0,0 +1,2 @@ +org.springframework.beans.factory.aot.BeanRegistrationAotProcessor=\ +org.test.aot.RegisteredViaFactoriesBeanRegistrationAotProcessor \ No newline at end of file