The typical approach is to use a folder per environment that contains identically named files:
conf/dev/application.properties
conf/test/application.properties
conf/uat/application.properties
conf/prod/application.properties
Then parameterize the @PropertySource to use an environment variable such as "env":
@PropertySource("file:${catalina.base}/conf/${env}/application.properties")
Now you simply need to ensure that in each environment there is an operating system environment variable defined called "env" that is set correctly (i.e, dev, test, uat or prod). You haven't stated how your launch your application (j2ee server launch script on linux? springboot docker container?) but it shouldn't be a problem to set the environment variable in each environment.
Rather than load files from "${catalina.base}" you can load files from the classpath with something like:
// see https://stackoverflow.com/a/26387933/329496
@PropertySource("classpath:${env}/application.properties")
If you are building with maven you can create the folders under src/main/resources
and they will be copied into the classpath:
src/main/resources/dev/application.properties
src/main/resources/test/application.properties
src/main/resources/uat/application.properties
src/main/resources/prod/application.properties
That simplified things quite a lot. Jenkins doesn't need to do anything at all as the same build/release artefact can be run in any and every environment. It is usually an anti-pattern to have Jenkins have to do extra work to reconfigure an application to work in a particular environment. Rather you should aim to have a single Jenkins release job build an artefact that can be simply copied between environments.
There is one downside to such an approach. If you want to create a new environment you have to add a new file into source control and create a new release artefact. With legacy technologies setting up new environments is a lot of work (e.g., commissioning VMs and databases) such that this "extra step" of adding a file to the code doesn't seem like a problem.
With more modern cloud-native technologies like Kubernetes, you can set up environments in seconds, and "on-demand". In which case you don't want to be pre-specifying your environments within your codebase. Rather you should follow a 12factor.net approach and define every single property as an environment variable and have spring just use them directly. If you look at the current springboot documentation it uses environment variables by default. This means if you haven't defined a @PropertySource
but you are using things like:
@Value("${name}")
private String name;
they will bbe set from the environment variable of the same name but using all caps. This then moves the problem to "how do we make sure that the environment variables are correctly set up in each environment". Technologies that make it easy to spin up environments in seconds (kubernetes, cloudfoundry, swarm) also typically make it easy to manage environment variables. With kubernetes you create a "ConfigMap" (e.g. "my-app-properties") and have it mounted as the environment variables where every key+value defines a unique environment variable for the application. Each logical environment can then be a separate Kubernetes namespace (possibly on a shared cluster for dev/test but a dedicated cluster for prod) that has its own "my-app-properties" configuration object that defines the environment variables for that logical environment .
You can put these settings under source control but not in the application source repo. Rather you can take an "infrastructure as code" approach where everything that runs the application (e.g., all scripts and yaml to set up the environments) is in it's own separate git repo. You can then set up a configuration deploy job that is triggered by a git webhook on the configuration repo. This can push out the new environment variables. That allows you to have continuous deployment of configuration changes that are independent of the continuous deployment of application code.
If you are using Kubernetes then Helmfile is an excellent choice for that is it won't update anything in kubernetes that hasn't changed within git. This means it is safe to reapply all the configuration in the git repo on every push to a protected master branch; helmfile will double check what is already deployed and only update Kubernetes configuration objects that need to be updated.