Spring Boot Profiles for DEV and PROD Environments

This article applies to sites created with the Spring Boot framework, using Apache Maven as the build tool. In order to demonstrate how profiles work, we'll visit an example using Google Analytics and Google Tag Manager for tracking site metrics. I use this method for my website, Initial Commit, which is built using Spring Boot, the Thymeleaf template engine, and is hosted on AWS Elastic Beanstalk.

What are Spring Boot Profiles?

One of the core design principles behind Spring Boot is that it encourages convention over configuration. This means that the vast majority of app configurations use sensible default values that can be overridden when necessary, but in general a Spring Boot app will work out of the box with no custom configuration required.

However, usually some customization is necessary and oftentimes we need environment specific customization. This is where profiles come in handy. A profile in Spring Boot can be thought of as a context that defines a specific set of app settings, variables, and behaviors. Each time the app is built, the developer can specify which profile to use. If no profile is specified, the default will be used.

In this article, we will create a DEV profile and PROD profile to enable environment-specific configuration properties.

We can easily define the profiles by adding the following XML to the project's pom.xml file:

    <profiles>
        <profile>
            <id>dev</id>
            <properties>
                <activatedProperties>dev</activatedProperties>
            </properties>
            <activation>
                <activeByDefault>true</activeByDefault>
            </activation>
        </profile>
        <profile>
            <id>prod</id>
            <properties>
                <activatedProperties>prod</activatedProperties>
            </properties>
        </profile>
    </profiles>

Note the <activeByDefault>true</activeByDefault> tag, which means that the development profile will be used by default assuming no profile is specified at build time.

Profiles work in conjunction with Spring Boot properties files. By default, Spring Boot parses a file called application.properties – located in the src/main/resources directory – to identify configuration information.

Our first task will be to add a parameter in that file which will tell Spring to use a different environment-specific property file corresponding to the active profile (i.e. the profile that the app is currently being run with). We can do this by adding the following to the application.properties file:

[email protected]@

Now we need to create the two new environment-specific property files (in the same path as the existing application.properties file), one to be used by the DEV profile and one to be used by the PROD profile. These files need to be named the following:

  • application-dev.properties
  • application-prod.properties

Within each of these files, properties can be defined that will only be applied when the corresponding profile is active.

In order to make this concept clearer, let's consider a real-world example involving Google Analytics and Google Tag Manager configuration on a Spring Boot site, which I'll go over in detail in the next few sections.

Overview of Google Analytics and Google Tag Manager

If you are a web developer with an active site, it is a good bet that you use the Google platform for your analytics, event tracking, conversion tracking, and search engine ranking statistics.

The following free Google tools provide these services:

  1. Google Analytics
  2. Google Tag Manager
  3. Google Search Console

Google Analytics links up to your domain and tracks a wide breadth of statistics including page views, session information, audience location, and event triggers in historical and real-time contexts.

Google Tag Manager integrates with Google Analytics and allows developers to define the events that get tracked by Google Analytics. This is done by defining Tag Manager listeners that link up to specific HTML elements on your website, which wait for the specified events (like a click or scroll) to occur. When a site user executes the event, a call gets made to Google Analytics, which logs the event.

Google Search Console provides information about how Google crawls your site, how many sites link back to yours, and where your site ranks based on search keywords.

If you've used these tools before, you'll know that in order to link your website to your Google Analytics account, Google provides a Tracking ID in the following format:

UA-123456789-1

This tracking ID gets embedded into your site via an HTML code snippet provided by Google:

<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-123456789-1"></script>
<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());

  gtag('config', ‘UA-123456789-1');
</script>

Similarly, Google Tag Manager provides a GTM ID in the following format:

GTM-ABCDEF1

This also needs to be embedded into your site via an HTML code snippet:

<!-- Google Tag Manager -->
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-ABCDEF1');</script>
<!-- End Google Tag Manager -->

<!-- Google Tag Manager (noscript) -->
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-ABCDEF1"
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<!-- End Google Tag Manager (noscript) -->

Configuring Tracking IDs with Profiles

Now, this is all well and good – users who interact with the site will trigger data to be logged into the linked Google Analytics account. However, since the tracking IDs are hard-coded into the HTML pages, data will be tracked even when developers and testers load up these pages in development and test versions of the site. This will skew the data and inflate the actual site statistics.

In order to solve this problem, we can set up dummy values for the tracking IDs in the development profile so that no data is logged to Google when the site pages are accessed in the lower environments.

The DEV profile will define dummy values for the Google Analytics and Tag Manager tracking IDs, and the PROD profile will use the real values. We will then replace the hard-coded values in the HTML templates with the dynamic property value.

In the DEV properties file, application-dev.properties, we can add the dummy values for the Google Analytics and Tag Manager tracking IDs like this:

googleanalyticsid=UA-XXXXXXXXXX-1
googletagmanagerid=GTM-XXXXXX1

In the PROD properties file, application-prod.properties, we can add the actual values for the Google Analytics and Tag Manager tracking IDs:

googleanalyticsid=UA-123456789-1
googletagmanagerid=GTM-ABCDEF1

The final step is to replace the hard coded reference to the tracking IDs in the HTML templates. Using the Thymeleaf template engine, this can be done by replacing the hard-coded Google Analytics tracking ID with:

${@environment.getProperty('googleanalyticsid')}

and the Google Tag Manager tracking ID with:

${@environment.getProperty('googletagmanagerid')}

The end result is that the code snippets provided by Google now look like this (note that due to nested quotes, double square brackets are added around the variable):

    <!-- Global site tag (gtag.js) - Google Analytics -->
    <script async th:src="'https://www.googletagmanager.com/gtag/js?id=' + ${@environment.getProperty('googleanalyticsid')}"></script>
    <script>
        window.dataLayer = window.dataLayer || [];
        function gtag(){dataLayer.push(arguments);}
        gtag('js', new Date());

        gtag('config', "[[${@environment.getProperty('googleanalyticsid')}]]");
    </script>

    <!-- Google Tag Manager -->
    <script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
    new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
    j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
    'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
    })(window,document,'script','dataLayer',"[[${@environment.getProperty('googletagmanagerid')}]]");</script>
    <!-- End Google Tag Manager -->

    <!-- Google Tag Manager (noscript) -->
    <noscript><iframe th:src="'https://www.googletagmanager.com/ns.html?id=' + ${@environment.getProperty('googletagmanagerid')}"
    height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
    <!-- End Google Tag Manager (noscript) -->

That's it! Now whenever the application is built and run without specifying the profile – for example from within an IDE like Spring Tool Suite, Eclipse, or IntelliJ – the DEV profile will be chosen by default and dummy values will be substituted for the Google tracking IDs. This will prevent any page loads and triggered events from logging data to the Google platform.

Specifying the Profile at Build Time

When building the application for production, we need to specify the PROD profile is to be used so that the real tracking ID values are used. Here is how this is done using Maven as the build tool:

$ mvn -Pprod clean install

The key to this command is the -P flag, which is used to specify the profile to use for the build.

If we want to set the profile after the code is built, we can use a Java VM argument at application launch. This is done as follows:

$ java –jar -Dspring.profiles.active=prod app.jar

Alternatively, the profile can be directly specified in the application.properties file by adding the line:

spring.profiles.active=prod

In each case, we specify prod as the active profile, which causes the application-prod.properties file to be chosen for configuration purposes. Since this file contains the real values for the tracking IDs, those will be inserted into the templates in the production build and user events will be successfully tracked on the Google platform.

About the Author

This article was written by Jacob Stopak, a software consultant and developer with passion for helping others improve their lives through code. Jacob is the creator of Initial Commit - a site dedicated to helping curious developers learn how their favorite programs are coded. Its featured project helps people learn Git at the code level.

Author image
About Jacob Stopak
San Diego, CA Twitter Website
Jacob Stopak is a software developer and creator of InitialCommit.io - a site dedicated to teaching people how popular programs are coded. Its main project helps people learn Git at the code level.