Speed up development mode startup and production builds by not scanning dependencies that are not marked as Vaadin add-ons.
Background
There are multiple features that need whole-world knowledge when starting a Vaadin application in development mode. Some prominent examples include:
@NpmPackage
for updatingpackage.json
before runningnpm install
@Route
and@Menu
for the route registry@JsModule
forgenerated-flow-imports.js
@BrowserCallable
and@Endpoint
for the Hilla generator
The production build additionally also traverses bytecode references from identified entry points to collect data for generating an optimized production mode bundle where frontend dependencies are grouped for lazy loading based on Flow views.
Problem
Scanning the entire classpath to find these resources slows down initialization. This is particularly problematic for development mode since the startup time has an immediate impact on developer experience. There’s also a risk of the inverse problem with the Spring Boot approach that explicitly limits scanning to the application’s own resources which instead means that annotations from 3rd party add-ons will not work without additional configuration.
Being more selective in the bytecode traversal done for the production build is also beneficial for performance even though this has a smaller impact since production builds are not created as frequently and are typically run on a CI server.
Solution
Most of the scanning time is spent on classes in 3rd party dependencies that have no relationship at all with Vaadin. Scanning could be significantly faster if we would only look in the application’s own sources and in dependencies that are explicitly marked as Vaadin add-ons. Vaadin Directory requires a Vaadin-Package-Version: 1
entry in META-INF/MANIFEST.MF
and this can also be used as an indicator that the dependency should be considered by the scanner. The number of classes to investigate could be significantly reduced by ignoring classes in jar files without that manifest entry.
Dependencies without that manifest entry may still include classes that should be considered by Vaadin in the case of company-internal add-ons or multi-module project setups. Such cases could be solved either by adding the manifest entry or by making the main application code reference any missing classes using the @Uses
annotation. @Uses
is currently only supported for cases related to UI components but not e.g. for @BrowserCallbable
annotations. We should extend the definition of the annotation to cover all cases and then also move it since the current com.vaadin.flow.component.dependency
package implies that it applies only for Flow components. The existing configuration parameters to allow or block scanning of specific packages can also be used to explicitly scan classes that would otherwise be ignored.
Transition strategy
This solution is a small breaking change in the case of unpublished add-ons or multi-module project setups. To avoid surprises, we should change the default behavior only in the next major version (i.e. Vaadin 25). We can still introduce the new functionality on an opt-in basis in a minor release and automatically opt in newly generated applications from start.vaadin.com. This would be in the form of introducing a new property, vaadin.annotationScanner
, for application.properties
and related configuration sources. This same property could also be used by Vaadin 25 applications that want to keep using the old behavior.
The supported values are:
add-on
ignores dependency jars that do not have a manifest that definesVaadin-Package-Version
. At runtime, the class of any attached component instance is checked for annotations. If the class has a relevant annotation and it was not detected by the scanner, then an exception is thrown in development mode and a warning is logged in production mode to make the developer aware that their component might not work as expected without additional configuration.full
works like in Vaadin 24.7. Also checks whether the annotated classes detected by the scanner would have been detected also in add-on mode. If no such class is detected, an info message is logged to encourage the developer to change the configuration option to improve performance. If any such class is detected, then a warning is logged that lists the detected classes/jar files and explains that full scanning mode will be removed in the future. In Vaadin 24.x, the warning also mentions that the default will change in Vaadin 25 iffull
is not explicitly defined but only used because it’s the default.
The default option in Vaadin 24.x is full
and the default in Vaadin 25+ is add-on
. We will remove support for full
and thus also remove the configuration option in a future major version (i.e. Vaadin 26 or later).
Notes
There is a possibility of going further to completely eliminate the need for scanning dependency classes. One potential approach would be to cache the scanning results somewhere in the project directory (e.g. inside /target/
) to avoid scanning again as long as the timestamp on the jar file has not changed.
We could also add a requirement that add-ons contain predefined metadata in the form of a Jandex index or an explicit list of classes similar to AutoConfiguration.imports
in Spring Boot. This kind of change could provide ultimate performance but it would also require updates to all Directory add-ons. This in turn would necessitate a more elaborate transition strategy.