How can I create a Spring 5 component index?
Asked Answered
S

3

22

Spring Framework 5 apparently contains support for a "component index" which lives in META-INF/spring.components and can be used to avoid the need for class-path scanning, and thus, I assume, improve a webapps' startup time.

See:

How can I create such a component index for an existing web app I plan to upgrade to Spring 5?

(Ideally it would get generated automatically at build time with Maven I imagine, but any other workable approaches would at least give me a starting point to work from)

Scenarist answered 12/11, 2017 at 22:52 Comment(7)
Have you already checked out Stephane Nicoll's repo, which goes through some test examples? These examples appear to utilize a dependency in the pom to build this index based on the annotated @ComponentsSnip
Ah, no, I'd not found that - Thanks, I'll have a look!Scenarist
Did you have any luck with it? I tried including the spring-context-indexer in my pom, but didn't see any difference at all in loading time, and I didn't see a index file created. So far I haven't been able to get this workingMyrt
Haven't gotten back to it yet sorry - I'll be sure to report back if/when I do.Scenarist
@Myrt it worked great for me. It is attached to a Maven goal, specifically the package goal, so all I had to do was run mvn package and it created my index file.Decorum
Ok thanks, maybe that was my problem, I'm not sure when running in dev mode if it actually runs packageMyrt
Hardly any documentation exists for this. The file created is in WEB-INF/classes/META-INF/spring.components in the resulting war file using the maven War plugin. If you have an incomplete index, you will get a bean not found error. I have multiple modules dependent on each other and it is not scanning/indexing the dependent modules, so I get an error at startup.Handiwork
R
18

Spring 5 Has added a new feature to improve startup performance of large applications.

it creates a list of component candidates at compilation time.

In this mode, all modules of the application must use this mechanism as, when the ApplicationContext detects such index, it will automatically use it rather than scanning the classpath.

To generate the index, we just need to add below dependency to each module

Maven:

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context-indexer</artifactId>
        <version>5.0.3.RELEASE</version>
        <optional>true</optional>
    </dependency>
</dependencies>

Gradle

dependencies {
    compileOnly("org.springframework:spring-context-indexer:5.0.3.RELEASE")
}

This process will generate a META-INF/spring.components file that is going to be included in the jar.

Reference : 1.10.9. Generating an index of candidate components

Rural answered 23/1, 2018 at 17:51 Comment(6)
Yeah - Interesting - I'll give it a shot at some point. It seems super odd that the mechanism here (at least in maven) is adding a dependency to the module though - I'd have expected it to be a maven plugin or something like that. When exactly does the generation occur in the build process?Scenarist
If you read the documentation it says it needs annotation-processor in the IDE: When working with this mode in your IDE, the spring-context-indexer must be registered as an annotation processor to make sure the index is up to date when candidate components are updated. ! I don't think it's integrated with maven :-(Ashjian
Finally got back to give this a try (with <scope>provided</scope> rather than optional=true if it matters), and it does indeed cause a spring.compoents file to be created - I guess adding the dependency causes the annotation processor to apply (and scope=provided avoids actually packing up the jar). Unfortunately for some reason the resulting war fails to deploy with a "No qualifying bean" exception, but that's a different issue - Thanks.Scenarist
Ah, my issue there seems to have been that I needed all the dependencies which also created beans to have spring.components too, so I have it working after ensuring that - Sadly though, it seems to have very little impact on the startup time overall (looks to be saving an average of about 1 second at best).Scenarist
But for jar dependency, this does not work. Can you please look at this #53718684Ossa
In Gradle 4.6+ you may add the dependency as an annotationProcessor. See docs.gradle.org/4.6/…Indefatigable
G
16

The META-INF/spring.components files are generated by an annotation processor library called spring-context-indexer. If you add this library as "annotation processor path" to the maven-compiler-plugin, the files will be generated automatically at build time:

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-compiler-plugin</artifactId>
  <configuration>
    <annotationProcessorPaths>
      <path>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context-indexer</artifactId>
        <version>5.0.6.RELEASE</version>
      </path>
    </annotationProcessorPaths>
    ...
  </configuration>
</plugin>

This setup requires maven-compiler-plugin version 3.5 or greater.

See also: https://maven.apache.org/plugins/maven-compiler-plugin/compile-mojo.html#annotationProcessorPaths

Gessner answered 23/5, 2018 at 10:11 Comment(1)
How to generate the Spring Component Index when building with Maven and Kotlin:Dorm
O
0

Kotlin + Maven:

To generate the Spring Component Index when building with Maven and Kotlin:

Kotlin Maven Plugin includes Kapt - Kotlin Annotation Processing Tool. It has a goal kapt which needs to execute before compile (it uses the sources, not the bytecode.

See also:

I put the execution into a profile, so that I can get rid of this if not needed.

mvn install -PcreateSpringComponentIndex

Important: You need to keep this updated for each compilation as a matter of habit, otherwise Spring won't pick the new(ly) annotated classes as components! That also means, that if skipping the generation, you need to mvn clean.

Important: When using "shading" (putting all classes and resources into a single flat jar), the files META-INF/spring.components need to be merged! Otherwise one of them will be picked randomly and Spring won't detect any other components. (It's better to avoid shading and pack the dependencies as JARs within a JAR).

Example:

    <!-- May speed up the app boot by a couple of seconds. See https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-scanning-index -->
    <profile>
        <id>createSpringComponentIndex</id>
        <build>
            <plugins>
                <plugin>
                    <groupId>org.jetbrains.kotlin</groupId><artifactId>kotlin-maven-plugin</artifactId><version>${kotlin.version}</version>
                    <executions><execution><id>kapt</id><goals><goal>kapt</goal></goals><phase>process-classes</phase></execution></executions>
                    <configuration>
                        <sourceDirs><sourceDir>src/main/kotlin</sourceDir><sourceDir>src/main/java</sourceDir></sourceDirs>
                        <annotationProcessorPaths>
                            <annotationProcessorPath><groupId>org.springframework</groupId><artifactId>spring-context-indexer</artifactId><version>5.3.23</version></annotationProcessorPath>
                        </annotationProcessorPaths>
                    </configuration>
                </plugin>
            </plugins>
        </build>
        <dependencies>
            <dependency><groupId>org.springframework</groupId><artifactId>spring-context-indexer</artifactId><version>5.3.23</version><scope>provided</scope></dependency>
        </dependencies>
    </profile>
Onassis answered 26/10, 2022 at 12:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.