Use CDN Service in Spring MVC

When I deployed TestZilla on Aliyun Elastic Compute Service, growing traffic made it worth offloading static files (images, CSS, and JavaScript) to a CDN. At the time there was little guidance on wiring a CDN into Spring MVC, so I asked on Stack Overflow and ended up with the setup below. The idea is simple: keep the CDN host in a single property and inject it wherever your JSP views reference static assets.

Note: This post has been updated for current Spring Framework versions (6.x / 7.x). It now uses version-less XSD schema locations and a single <util:properties> bean in place of the deprecated PropertyPlaceholderConfigurer the original 2015 version relied on. The technique still targets classic XML configuration and JSP views; in a new project the same “one property, injected into the view” idea maps directly onto Java config and a template engine.

Setup Spring Configuration

All we need is one bean that loads the properties file and exposes it by name. Add a <util:properties> bean to your Spring configuration (e.g. dispatcher-servlet.xml):

<util:properties id="propertyConfigurer" location="classpath:testzilla.properties"/>

This creates a java.util.Properties bean named propertyConfigurer, which we’ll reference from the view in a moment. Make sure the util namespace is declared on your root <beans> element. Note that the schema URLs are version-less, so Spring resolves them against whatever Spring version is on your classpath and there’s nothing to bump when you upgrade:

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:util="http://www.springframework.org/schema/util"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/util
        https://www.springframework.org/schema/util/spring-util.xsd">
    <!-- bean definitions -->
</beans>

If you also want ${cdn.url}-style placeholders resolved inside other bean definitions, add <context:property-placeholder location="classpath:testzilla.properties"/> along with the matching context namespace. For injecting the value into a JSP, the util:properties bean above is enough.

Create the Properties File

Create testzilla.properties on the classpath with the CDN host:

cdn.url=https://cdn.testzilla.org

Protocol-relative URLs (//cdn.testzilla.org) made sense when sites still mixed HTTP and HTTPS, but today everything is HTTPS, so prefer an absolute https:// URL. Leave off the trailing slash so it composes cleanly with the asset paths below.

Use the CDN URL in JSP

Now read the property with <spring:eval> and prepend it to every static asset URL:

<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<spring:eval expression="@propertyConfigurer.getProperty('cdn.url')" var="cdnUrl" />
<!DOCTYPE html>
<html lang="${language}">
<head>
    <meta charset="UTF-8">
    <title>Home | TestZilla</title>
    <link rel="stylesheet" type="text/css" href="${cdnUrl}/css/semantic.min.css" />
</head>
</html>

Here @propertyConfigurer refers to the bean we defined, and getProperty('cdn.url') pulls the value into the cdnUrl page variable. From there, ${cdnUrl}/css/semantic.min.css resolves to https://cdn.testzilla.org/css/semantic.min.css. Do the same for every CSS, JavaScript, and image reference, and switching CDN providers later becomes a one-line change in the properties file.

References