From 67edcde0a21a6f299d3dbf32a1882275c46b7498 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Deleuze?= Date: Thu, 21 Mar 2024 16:52:50 +0100 Subject: [PATCH] Introduce support for webjars-locator-lite This commit introduces support for org.webjars:webjars-locator-lite via a new LiteWebJarsResourceResolver in Spring MVC and WebFlux, and deprecates WebJarsResourceResolver which is performing a classpath scanning that slows down application startup. Closes gh-27619 --- framework-platform/framework-platform.gradle | 1 + spring-webflux/spring-webflux.gradle | 1 + .../config/ResourceChainRegistration.java | 15 +- .../resource/LiteWebJarsResourceResolver.java | 114 +++++++++++++ .../resource/WebJarsResourceResolver.java | 2 + .../config/ResourceHandlerRegistryTests.java | 7 +- .../LiteWebJarsResourceResolverTests.java | 153 ++++++++++++++++++ spring-webmvc/spring-webmvc.gradle | 1 + .../config/ResourcesBeanDefinitionParser.java | 1 + .../annotation/ResourceChainRegistration.java | 15 +- .../resource/LiteWebJarsResourceResolver.java | 112 +++++++++++++ .../resource/WebJarsResourceResolver.java | 6 +- .../web/servlet/config/MvcNamespaceTests.java | 1 + .../ResourceHandlerRegistryTests.java | 12 +- .../LiteWebJarsResourceResolverTests.java | 138 ++++++++++++++++ .../WebJarsResourceResolverTests.java | 1 + 16 files changed, 565 insertions(+), 15 deletions(-) create mode 100644 spring-webflux/src/main/java/org/springframework/web/reactive/resource/LiteWebJarsResourceResolver.java create mode 100644 spring-webflux/src/test/java/org/springframework/web/reactive/resource/LiteWebJarsResourceResolverTests.java create mode 100644 spring-webmvc/src/main/java/org/springframework/web/servlet/resource/LiteWebJarsResourceResolver.java create mode 100644 spring-webmvc/src/test/java/org/springframework/web/servlet/resource/LiteWebJarsResourceResolverTests.java diff --git a/framework-platform/framework-platform.gradle b/framework-platform/framework-platform.gradle index e921187bf35..1370e6fa7dd 100644 --- a/framework-platform/framework-platform.gradle +++ b/framework-platform/framework-platform.gradle @@ -143,6 +143,7 @@ dependencies { api("org.testng:testng:7.9.0") api("org.webjars:underscorejs:1.8.3") api("org.webjars:webjars-locator-core:0.55") + api("org.webjars:webjars-locator-lite:0.0.2") api("org.xmlunit:xmlunit-assertj:2.9.1") api("org.xmlunit:xmlunit-matchers:2.9.1") api("org.yaml:snakeyaml:2.2") diff --git a/spring-webflux/spring-webflux.gradle b/spring-webflux/spring-webflux.gradle index e0f98bcdc8f..29b7021f033 100644 --- a/spring-webflux/spring-webflux.gradle +++ b/spring-webflux/spring-webflux.gradle @@ -32,6 +32,7 @@ dependencies { optional("org.jetbrains.kotlin:kotlin-stdlib") optional("org.jetbrains.kotlinx:kotlinx-coroutines-reactor") optional("org.webjars:webjars-locator-core") + optional("org.webjars:webjars-locator-lite") testImplementation(testFixtures(project(":spring-beans"))) testImplementation(testFixtures(project(":spring-core"))) testImplementation(testFixtures(project(":spring-web"))) diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/config/ResourceChainRegistration.java b/spring-webflux/src/main/java/org/springframework/web/reactive/config/ResourceChainRegistration.java index 1102b974cde..376daf987aa 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/config/ResourceChainRegistration.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/config/ResourceChainRegistration.java @@ -27,6 +27,7 @@ import org.springframework.util.ClassUtils; import org.springframework.web.reactive.resource.CachingResourceResolver; import org.springframework.web.reactive.resource.CachingResourceTransformer; import org.springframework.web.reactive.resource.CssLinkResourceTransformer; +import org.springframework.web.reactive.resource.LiteWebJarsResourceResolver; import org.springframework.web.reactive.resource.PathResourceResolver; import org.springframework.web.reactive.resource.ResourceResolver; import org.springframework.web.reactive.resource.ResourceTransformer; @@ -43,9 +44,12 @@ public class ResourceChainRegistration { private static final String DEFAULT_CACHE_NAME = "spring-resource-chain-cache"; - private static final boolean isWebJarsAssetLocatorPresent = ClassUtils.isPresent( + private static final boolean isWebJarAssetLocatorPresent = ClassUtils.isPresent( "org.webjars.WebJarAssetLocator", ResourceChainRegistration.class.getClassLoader()); + private static final boolean isWebJarVersionLocatorPresent = ClassUtils.isPresent( + "org.webjars.WebJarVersionLocator", ResourceChainRegistration.class.getClassLoader()); + private final List resolvers = new ArrayList<>(4); @@ -79,6 +83,7 @@ public class ResourceChainRegistration { * @param resolver the resolver to add * @return the current instance for chained method invocation */ + @SuppressWarnings("removal") public ResourceChainRegistration addResolver(ResourceResolver resolver) { Assert.notNull(resolver, "The provided ResourceResolver should not be null"); this.resolvers.add(resolver); @@ -88,7 +93,7 @@ public class ResourceChainRegistration { else if (resolver instanceof PathResourceResolver) { this.hasPathResolver = true; } - else if (resolver instanceof WebJarsResourceResolver) { + else if (resolver instanceof WebJarsResourceResolver || resolver instanceof LiteWebJarsResourceResolver) { this.hasWebjarsResolver = true; } return this; @@ -108,10 +113,14 @@ public class ResourceChainRegistration { return this; } + @SuppressWarnings("removal") protected List getResourceResolvers() { if (!this.hasPathResolver) { List result = new ArrayList<>(this.resolvers); - if (isWebJarsAssetLocatorPresent && !this.hasWebjarsResolver) { + if (isWebJarVersionLocatorPresent && !this.hasWebjarsResolver) { + result.add(new LiteWebJarsResourceResolver()); + } + else if (isWebJarAssetLocatorPresent && !this.hasWebjarsResolver) { result.add(new WebJarsResourceResolver()); } result.add(new PathResourceResolver()); diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/resource/LiteWebJarsResourceResolver.java b/spring-webflux/src/main/java/org/springframework/web/reactive/resource/LiteWebJarsResourceResolver.java new file mode 100644 index 00000000000..b628d438c68 --- /dev/null +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/resource/LiteWebJarsResourceResolver.java @@ -0,0 +1,114 @@ +/* + * Copyright 2002-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.web.reactive.resource; + +import java.util.List; + +import org.webjars.WebJarVersionLocator; +import reactor.core.publisher.Mono; + +import org.springframework.core.io.Resource; +import org.springframework.lang.Nullable; +import org.springframework.web.server.ServerWebExchange; + +/** + * A {@code ResourceResolver} that delegates to the chain to locate a resource and then + * attempts to find a matching versioned resource contained in a WebJar JAR file. + * + *

This allows WebJars.org users to write version agnostic paths in their templates, + * like {@code