Component index vs classpath scanning in Spring 5 [duplicate] - java

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:
The "what's new in spring 5" mention
The jira issue under which the support was developed
Some examples of what the spring.components format seems to be from the change implementing it
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)

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

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

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:
https://kotlinlang.org/docs/kapt.html#using-in-maven
https://maven.apache.org/ref/3.8.6/maven-core/lifecycles.html
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>

Related

How can I create a Spring 5 component index?

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:
The "what's new in spring 5" mention
The jira issue under which the support was developed
Some examples of what the spring.components format seems to be from the change implementing it
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)
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
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
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:
https://kotlinlang.org/docs/kapt.html#using-in-maven
https://maven.apache.org/ref/3.8.6/maven-core/lifecycles.html
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>

Packaging war with new gwt maven plugin (having migrated from default maven plugin)

I've stated using Thomas Broyer's gwt maven plugin as it allows me to run gwt 2.8-rc2. I've got it running with the codeserver fine and with minimum effort.
However now I'm trying to figure out how to use it to do a full compile and package.
Simply running maven install (I expected this to work as it does work with the default) does not actually run the gwt compile.
Then it talks about various packaging formats etc and I'm not sure why these are necessary?
I assume someone has got this plugin packaging the war and has also migrated from the original plugin...
This is my plugin config - I am using skipModule as I've already got a module configured the way the other plugin expects.
<plugin>
<groupId>net.ltgt.gwt.maven</groupId>
<artifactId>gwt-maven-plugin</artifactId>
<version>1.0-rc-6</version>
<extensions>true</extensions>
<configuration>
<moduleName>com.afrozaar.ashes.web.AshesWeb-safari</moduleName>
<skipModule>true</skipModule>
<style>DETAILED</style>
<!-- <logLevel>DEBUG</logLevel> -->
<classpathScope>compile+runtime</classpathScope>
</configuration>
<dependencies>
<dependency>
<groupId>com.google.gwt</groupId>
<artifactId>gwt-user</artifactId>
<version>2.8.0-rc2</version>
</dependency>
<dependency>
<groupId>com.google.gwt</groupId>
<artifactId>gwt-dev</artifactId>
<version>2.8.0-rc2</version>
</dependency>
<dependency>
<groupId>com.google.gwt</groupId>
<artifactId>gwt-servlet</artifactId>
<version>2.8.0-rc2</version>
</dependency>
</dependencies>
</plugin>
You're missing "executions" in your plugin configuration to run the compile goal (works the same as with the CodeHaus plugin).
My plugin works better when you separate client and server code into distinct Maven modules, which is why this setup is not clearly documented (because I actively discourage it). You can have a look at the samples in the GWT git repository to find examples similar to your case though.
BTW, I believe you can use rc2 with the CodeHaus plugin rc1; that's probably why you added those dependencies, which are useless with my plugin.
See also https://tbroyer.github.io/gwt-maven-plugin/migrating.html

Does Maven have a way to get a dependency version as a property?

I'm using a BOM to import dependencies from another project to mine, and I need a way to reference a dependency's version that is already declared in said BOM. So far, I've attempted to list the dependency version as a property in the BOM, but this approach fails because properties don't get imported with BOMs.
I've seen where the Dependency Plugin's dependency:properties goal does almost exactly what I need, but instead of giving me a full path of the artifact I need the version as a property. Is there something out there that can give me the version of a resolved artifact as a property?
UPDATE - 'Why not use a parent pom?'
I commonly find myself working in application server environments, where the dependencies provided are specified with BOM artifacts (as it appears that this has become a somewhat common/standard way to distribute groups of inter-related artifacts, i.e. widlfly). As such, I want to treat the BOM as the single source of truth. The idea of doing something like re-delcaring a dependency version property that has already been defined in a BOM seems incorrect.
If I were to define properties in a parent pom that mirrored an application server's environment, I now have to worry about keeping parent pom properties and BOM properties in sync - why even have a BOM at all at that point?
The information is already available on the dependency tree, it's just a matter of exposing it...
Couldn't find any existing maven or plugin functionality for this, so I forked the old dependencypath-maven-plugin and altered it to use versions. Now I can drop in a plugin like this:
<build>
.
.
<plugins>
.
.
<plugin>
<groupId>io.reformanda.semper</groupId>
<artifactId>dependencyversion-maven-plugin</artifactId>
<version>1.0.0</version>
<executions>
<execution>
<id>set-all</id>
<goals>
<goal>set-version</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
And access properties like this:
groupId:artifactId:type[:classifier].version
I.E.
io.undertow:undertow-core:jar.version=1.3.15.Final
Check out the README for more info on how to use the plugin. It's available # Maven Central:
<dependency>
<groupId>io.reformanda.semper</groupId>
<artifactId>dependencyversion-maven-plugin</artifactId>
<version>1.0.0</version>
</dependency>
... plugins all the way down ...
Short answer - yes, you can.
In details, your root pom.xml:
<properties>
<slf4j.version>1.7.21</slf4j.version>
</properties>
...
<dependencyManagement>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
...
</dependencyManagement>
In modules pom.xml:
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
...
</dependencies>
Also you can use ${slf4j.version} value to filter resources or in plugin configurations.
Update
In case you cannot use properties in the parent POM, you can either
retreive all dependencies and their versions with dependency:list plugin; or
use together dependency:list + antrun:run plugin; or
configure CI server scripts to do it for you (e.g. with this example); or
write a custom plugin to handle your versions logic.
This maven plugin is on Github (https://github.com/semper-reformanda/dependencyversion-maven-plugin) and it is a must for anyone dealing with Dependency versions, for instance when using Webjars dependencies - you can inject Webjar version numbers directly into your web resources.
I had been looking for such a functionality for a long time, I hope more people come across it and that it gets up on Maven central (I actually think it should come with Maven out of the box)

Why aren't 'provided' Maven dependencies 'transitive'?

Why doesn't Maven inherit provided dependencies?
My situation:
I have 2 independent projects A and B.
I don't own project A.
A and B use a some of the same libraries:
reflections-0.9.9-RC1.jar
guava-11.0.2.jar
xml-apis-1.0.b2.jar
javassist-3.16.1-GA.jar
dom4j-1.6.1.jar
jsr305-1.3.9.jar
I made project C, which is a plugin for project A, but also uses project B.
Project C pom.xml:
<dependencies>
<dependency>
<groupId>com.a</groupId>
<artifactId>a</artifactId>
<version>1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.b</groupId>
<artifactId>b</artifactId>
<version>1.0</version>
<scope>compile</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.4.2</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
Now I want to make plugins for project C but I can't.
If I create project D with a dependency to project C,
it won't inherit the dependency to project A.
It will if I set the scope to compile but that would shade it into project C which is not useful and would cause duplicates.
So now I have to add dependency to both A and B with every plugin I make.
Compile -This is the default scope, used if none is specified. Compile dependencies are available in all classpaths of a project. Furthermore, those dependencies are propagated to dependent projects.
Provided - This is much like compile, but indicates you expect the JDK or a container to provide the dependency at runtime. For example, when building a web application for the Java Enterprise Edition, you would set the dependency on the Servlet API and related Java EE APIs to scope provided because the web container provides those classes. This scope is only available on the compilation and test classpath, and is not transitive.
Why not?
There is an open bug for that exact requirement: MNG-2205. It is currently in the backlog for version 3 of Maven but I wouldn't get your hopes up: it was created in April 2006 (!).
Quoting Jason van Zyl from that bug report:
It is unlikely we will change the behavior of the provided scope, but it would be possible to create a new 'provided-transitive' if we really wanted this. Changing the definition of existing scopes would be problematic.
Also, quoting Andrew Williams, still from that bug report:
if C wants to use Sybase JConnect then it must declare this as a dependency. A could at any time change it's dependencies and "break" this assumption of C's.
It is wrong to use a dependency that you do not declare.
There is no better answer to this question: the documentation is quite clear on the subject: provided dependencies are not currently transitive. The reason it was initially done this probably revolves around the fact that you should explicitely declare a dependency if you intend to use it.

Java: How do I build standalone distributions of Maven-based projects?

I often encounter distributions of Java applications or libraries which
use Maven as their build tool.
Some of them, sadly, don't provide standalone (or redistributable) jars.
Is it possible to build Maven-based applications in such a way, that
the build result contains all dependencies and can be redistributed to work out-of-the box?
I tried to build Jackrabbit's OCM module.
For some very "intelligent" reasons there is no downloadable standalone
version.
So I built Jackrabbit with Maven (the source package of Jackrabbit includes
OCM), and got the same jar as found in the apache repository.
The jar doesn't contain necessary dependencies and is useless to me.
As Dominic said, using the assembly plugin will do the trick. You would usually configure it inside your own project's POM to gather and package all required dependencies:
...
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</plugin>
...
jar-with-dependencies is predefined by the assembly plugin and will include all dependencies in the final package (see the documentation here).
If you don't want to use Maven for your own project, you will need to modify the libraries' POMs and repackage them yourself (download the sources, add the above snippet to pom.xml and run mvn package). Beware of duplicate or incompatible transitive dependencies if you use multiple libraries. exclusions might help in that case (see documentation here).
Use the Maven Shade plugin
...but be careful of the gotchas (similar to the one described further down my answer), which has got a workaround explained here.
Also, be ultra careful with the Shade plugin config. I accidentally used double <configuration> tags once, and the transformers didn't apply at all, and the plugin also took the liberty of not warning me.
Don't use the Maven Assembly plugin
assembly:single will unpack your dependency JARs as-is, and this could not be what you want. E.g. stuff like META-INF/spring.schemas will be overridden with the last Spring dependency JAR that's evaluated, and as such your XSDs won't be found (apart from those in the last JAR, of course). Which is why systems like Alfresco made their AMP plugin which bundles dependencies inside lib/ inside the AMP you're building. The latter raises dependency management issues, though.
You may have some luck with the appassembler plugin. Failing that, take a look at the assembly plugin. That's more flexible, but lower level. If you're using the assembly plugin, you may find the chapter on it in maven: the definitive guide to be useful.
As a couple of the posters said, the assembly plugin is a good way of creating a complete jar file, with all project dependencies. However, you don't actually have to modify the pom.xml file. Simply run:
mvn assembly:single -DdescriptorId=jar-with-dependencies
... in order to create a jar file. If you want to do anything more advanced, you should probably modify pom.xml, and create a custom assembly descriptor.
Change the pom.xml file and use the <Embed-Dependency> directive. A similar example can be found here so you can adapt it to your scenario.
<Embed-Dependency>*;scope=!test;inline=true</Embed-Dependency>
I think this should do the trick.
Here is the example at the above URL that seems to give timeout.
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>br.gov.lexml</groupId>
<artifactId>toolkit</artifactId>
<packaging>bundle</packaging>
<version>3.0</version>
<parent>
<artifactId>lexml</artifactId>
<groupId>br.gov.lexml</groupId>
<version>1.0</version>
</parent>
<build>
<finalName>Lexml_Toolkit-2.0</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.5</source>
<target>1.5</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<instructions>
<!--_include>src/toolkit/resources/META-INF/MANIFEST.MF</_include-->
<Export-Package>*;-split-package:=merge-last</Export-Package>
<Bundle-Activator>br.gov.lexml.borda.Toolkit</Bundle-Activator>
<Bundle-Name>Toolkit</Bundle-Name>
<Private-Package />
<Embed-Dependency>*;scope=!test;inline=true</Embed-Dependency>
<Bundle-ClassPath>.,{maven-dependencies}</Bundle-ClassPath>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.apache.xmlbeans</groupId>
<artifactId>xmlbeans</artifactId>
<version>2.4.0</version>
</dependency>
<dependency>
<groupId>org.apache.xmlbeans</groupId>
<artifactId>xmlbeans-xmlpublic</artifactId>
<version>2.4.0</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.15</version>
</dependency>
<dependency>
<groupId>br.gov.lexmlbeans</groupId>
<artifactId>lexmlbeans</artifactId>
<version>3.0</version>
</dependency>
</dependencies>
</project>
I believe the Maven Shade Plugin will satisfy your needs. I use it when I am building command line interface tools to create an Uber JAR including my classes and along with the classes from all my dependencies.
Its very easy to use and I think this example is self-explanatory.

Categories

Resources