<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Mark Heckler</title>
    <description>The latest articles on DEV Community by Mark Heckler (@mkheck).</description>
    <link>https://dev.to/mkheck</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F332475%2F1a4bf795-ed18-4353-ac7d-ebb93d2f424d.png</url>
      <title>DEV Community: Mark Heckler</title>
      <link>https://dev.to/mkheck</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mkheck"/>
    <language>en</language>
    <item>
      <title>Cosmos DB for Spring Developers, Part II: Using Cosmos DB as a MongoDB Database</title>
      <dc:creator>Mark Heckler</dc:creator>
      <pubDate>Fri, 16 Dec 2022 13:52:58 +0000</pubDate>
      <link>https://dev.to/mkheck/cosmos-db-for-spring-developers-part-ii-using-cosmos-db-as-a-mongodb-database-4i88</link>
      <guid>https://dev.to/mkheck/cosmos-db-for-spring-developers-part-ii-using-cosmos-db-as-a-mongodb-database-4i88</guid>
      <description>&lt;p&gt;&lt;a href="https://dev.to/mkheck/cosmos-db-for-spring-developers-part-i-using-cosmos-db-as-a-sql-database-28me"&gt;In the first installment of this series&lt;/a&gt;, I discussed Spring Boot, Spring Data, and several of the underlying datastores supported by Spring Data. I also introduced Azure Cosmos DB and offered a glimpse of the various APIs it supports. I then showed how to create a Spring Boot application that uses Cosmos DB as a SQL database.&lt;/p&gt;

&lt;p&gt;If you're using a SQL datastore, I trust that was a really useful introduction to how to take your existing Spring Boot relational database application and make it truly planetary scale. But what if you prefer a non-relational (NoSQL) document database? Short of rewriting your apps, are you constrained by what that particular vendor chooses to offer? Absolutely not!&lt;/p&gt;

&lt;p&gt;In this installment, I'll show you how to use Cosmos DB as a MongoDB database.&lt;/p&gt;

&lt;h2&gt;
  
  
  Azure Cosmos DB for MongoDB
&lt;/h2&gt;

&lt;p&gt;As mentioned in Part I, &lt;a href="http://aka.ms/cosmosdb"&gt;Azure Cosmos DB&lt;/a&gt; is a planetary-scale datastore that offers various ways and APIs to manage your critical data and provide access to it at breathtaking speed. Harnessing the power of Cosmos DB as a SQL datastore was a fairly straightforward affair, but here's a pleasant surprise: replacing a MongoDB database in your Spring Boot application is even easier.&lt;/p&gt;

&lt;p&gt;Aside from the rather cumbersome (yet delightfully descriptive) name, &lt;strong&gt;Azure Cosmos DB for MongoDB&lt;/strong&gt; is a simple, yet powerful, drop-in replacement for MongoDB in your Spring Boot apps. To illustrate this, I'll create another project very similar to the one I created in Part I, but using MongoDB as the initial choice of database.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a Spring Boot app
&lt;/h2&gt;

&lt;p&gt;There are many ways to begin creating a Spring Boot application, but I prefer to begin with the &lt;a href="https://start.spring.io"&gt;Spring Initializr&lt;/a&gt;. Choose your build system (I opt for Maven most days, but your -- and my -- mileage may vary), choose a meaningful artifact name, change the group name if you so choose, then select the dependencies as shown in the following screen capture.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create and open the project
&lt;/h3&gt;

&lt;p&gt;NOTE: All code is in a Github repository linked at the bottom of this article.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--juFP2BQr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7mu1l1oc7gux38415xg3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--juFP2BQr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7mu1l1oc7gux38415xg3.png" alt="Image description" width="880" height="573"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I use only three dependencies for this example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reactive Web - includes all dependencies to create web apps using reactive streams, non-blocking and/or imperative APIs&lt;/li&gt;
&lt;li&gt;Spring Data Reactive MongoDB - enables your app to access MongoDB using the Reactor (reactive streams) API&lt;/li&gt;
&lt;li&gt;Lombok - boilerplate-reducing library, useful for domain classes, logging, and more&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Next, click the "Generate" button to generate the project structure and download the compressed project files (.zip). Once downloaded, go to the directory where the file was saved, decompress it, and open the project in the Integrated Development Environment (IDE) or text editor of your choice. I use IntelliJ IDEA and Visual Studio Code (VSCode) for nearly all my dev work, and for this article I'll open the project in IntelliJ by navigating (using the Mac Finder) into the expanded project directory and double-clicking on the Maven build file, &lt;em&gt;pom.xml&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Once the project is loaded, you can verify that the dependencies chosen from the Spring Initializr are present within the project by opening the &lt;em&gt;pom.xml&lt;/em&gt; file. There will be additional ones brought in for testing, etc., but these represent the three we selected before generating the project structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;org.springframework.boot&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;spring-boot-starter-webflux&amp;lt;/artifactId&amp;gt;
&amp;lt;/dependency&amp;gt;
&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;org.springframework.boot&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;spring-boot-starter-data-mongodb-reactive&amp;lt;/artifactId&amp;gt;
&amp;lt;/dependency&amp;gt;
&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;org.projectlombok&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;lombok&amp;lt;/artifactId&amp;gt;
    &amp;lt;optional&amp;gt;true&amp;lt;/optional&amp;gt;
&amp;lt;/dependency&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Code the application
&lt;/h3&gt;

&lt;p&gt;This is a fairly simple (and a bit contrived) example, but I have expanded it a bit from the previous article in this series. Check back for the next installment to see where things go from here.&lt;/p&gt;

&lt;h4&gt;
  
  
  The domain
&lt;/h4&gt;

&lt;p&gt;First, I code a domain class. In this example, I create a &lt;code&gt;User&lt;/code&gt; class with an &lt;code&gt;id&lt;/code&gt;, &lt;code&gt;firstName&lt;/code&gt;, &lt;code&gt;lastName&lt;/code&gt;, and &lt;code&gt;address&lt;/code&gt; member variables. This class and its properties are annotated thusly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;@Document&lt;/code&gt; - indicates that each instance of this class can be treated as a document by the underlying datastore, allowing for CRUD (Create, Read, Update, and Delete) operations&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@Data&lt;/code&gt; - Lombok annotation, instructs Lombok to consider this a "data class" and generate accessors (getters) and mutators (setters) for each member variable, along with &lt;code&gt;equals()&lt;/code&gt;, &lt;code&gt;hashCode()&lt;/code&gt;, and &lt;code&gt;toString()&lt;/code&gt; methods&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@NoArgsConstructor&lt;/code&gt; - Lombok annotation that instructs Lombok to generate a zero-argument constructor&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@RequiredArgsConstructor&lt;/code&gt; - Lombok annotation that instructs Lombok to generate a constructor with a parameter for each "required" member variable, as designated by the &lt;code&gt;@NonNull&lt;/code&gt; member variable annotation&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@Id&lt;/code&gt; - indicates which member variable corresponds to the underlying table's primary key&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@NonNull&lt;/code&gt; - addressed under &lt;code&gt;@RequiredArgsConstructor&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  The repository
&lt;/h4&gt;

&lt;p&gt;Spring Boot's autoconfiguration extends the power of Spring Data. By placing a database driver in your classpath (including it as a dependency in your build file results in its inclusion for deployment) and &lt;code&gt;extend&lt;/code&gt;ing a Spring Data-derived interface in your application code, Spring Boot's autoconfiguration creates the beans necessary to provide a proxy to the desired underlying datastore. In our case, this is all we need to provide foundational reactive database capabilities:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;interface UserRepository extends ReactiveCrudRepository&amp;lt;User, String&amp;gt; {}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this single line of code, we are defining an interface called &lt;code&gt;UserRepository&lt;/code&gt; that will inherit and potentially extend the capabilities of the &lt;code&gt;ReactiveCrudRepository&lt;/code&gt;, storing objects of type &lt;code&gt;User&lt;/code&gt; with identifiers (IDs) of type &lt;code&gt;String&lt;/code&gt;. Spring Boot's autoconfiguration does the rest.&lt;/p&gt;

&lt;p&gt;NOTE: We can do more, of course, defining custom query methods and more. But for this example, the provided functionality is sufficient.&lt;/p&gt;

&lt;h4&gt;
  
  
  The API
&lt;/h4&gt;

&lt;p&gt;Next, I define the Application Programming Interface (API) that provides the means and structure for external applications to interact with this service. Since this is our second app in this series, I expanded on the previous API a bit by adding a second &lt;code&gt;GET&lt;/code&gt; endpoint and method to get the first user document stored, along with a &lt;code&gt;POST&lt;/code&gt; endpoint and method, allowing for the creation and storage of a new user.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@RestController
@AllArgsConstructor
class CosmosMongoController {
    private final UserRepository repo;

    @GetMapping("/")
    Flux&amp;lt;User&amp;gt; getAllUsers() {
        return repo.findAll();
    }

    @GetMapping("/oneuser")
    Mono&amp;lt;User&amp;gt; getFirstUser() {
        return repo.findAll().next();
    }

    @PostMapping("/newuser")
    Mono&amp;lt;User&amp;gt; addUser(@RequestBody User user) {
        return repo.save(user);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To revisit some fundamentals, the &lt;code&gt;@RestController&lt;/code&gt; annotation is provided by the Spring Framework and combines the functionality of &lt;code&gt;@Controller&lt;/code&gt;, to respond to requests, and &lt;code&gt;@ResponseBody&lt;/code&gt;, to make the resultant object(s) the response body itself, rather than just providing access to the object(s) via a model variable, as is the typical MVC methodology.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;@AllArgsConstructor&lt;/code&gt; annotation instructs Lombok to create a constructor for the &lt;code&gt;CosmosMongoController&lt;/code&gt; class with a parameter (and thus required argument) for every member variable. Since the only member variable is a &lt;code&gt;UserRepository&lt;/code&gt;, Lombok generates a ctor with that single parameter.&lt;/p&gt;

&lt;p&gt;NOTE: &lt;code&gt;@AllArgsConstructor&lt;/code&gt; has the fortunate (or perilous) capability to update your constructor automatically if you simply add/remove member variables, so remember: With great power comes great responsibility. If you don't want this behavior, use &lt;code&gt;@RequiredArgsConstructor&lt;/code&gt; instead, annotating each member variable with &lt;code&gt;@NonNull&lt;/code&gt; that you wish to require and thus have represented as a parameter in the constructor.&lt;/p&gt;

&lt;h4&gt;
  
  
  The data
&lt;/h4&gt;

&lt;p&gt;As in Part I, I create a Spring bean using the &lt;code&gt;@Component&lt;/code&gt; annotation to populate some sample data. The &lt;code&gt;@Component&lt;/code&gt; annotation instructs Spring Boot, upon application initialization, to create an instance of the annotated class and place that object (bean) into the application context, i.e. its Dependency Injection (DI) container. All beans in the DI container are managed by the Spring Boot application in terms of lifecycle and, of course, injection into other code as dependencies.&lt;/p&gt;

&lt;p&gt;Once the bean is constructed, the &lt;code&gt;loadData()&lt;/code&gt; method is executed automatically due to the &lt;code&gt;@PostConstruct&lt;/code&gt; annotation. In this application, the &lt;code&gt;loadData()&lt;/code&gt; method deletes all lingering data in the underlying datastore (from previous app executions), populates it with two sample &lt;code&gt;User&lt;/code&gt; records, returns all &lt;code&gt;User&lt;/code&gt; records now stored in the database, and logs them to the console for verification.&lt;/p&gt;

&lt;p&gt;NOTE: The Reactive Streams API and the implementation of it as provided by Spring WebFlux/Project Reactor is beyond the scope of this particular article. Please consult the appropriate documentation at the &lt;a href="https://docs.spring.io/spring-framework/docs/current/reference/html/web-reactive.html"&gt;'Web on Reactive Stack' Spring documentation site&lt;/a&gt;, any of several sessions I've delivered available via &lt;a href="https://www.youtube.com/c/MarkHeckler"&gt;my YouTube channel&lt;/a&gt;, or by visiting the &lt;a href="http://www.reactive-streams.org/"&gt;Reactive Streams&lt;/a&gt; and &lt;a href="https://projectreactor.io/"&gt;Project Reactor&lt;/a&gt; sites.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Slf4j
@Component
@AllArgsConstructor
class DataLoader {
    private final UserRepository repo;

    @PostConstruct
    void loadData() {
        repo.deleteAll()
                .thenMany(Flux.just(new User("Alpha", "Bravo", "123 N 45th St"),
                        new User("Charlie", "Delta", "1313 Mockingbird Lane")))
                .flatMap(repo::save)
                .thenMany(repo.findAll())
                .subscribe(user -&amp;gt; log.info(user.toString()));
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Demo, take 1
&lt;/h2&gt;

&lt;p&gt;NOTE: This demo assumes you have a MongoDB instance running locally on your machine. If you do not, please install and run MongoDB locally or better yet, use the MongoDB Docker script provided at the end of this article to spin up a MongoDB instance in a Docker container.&lt;/p&gt;

&lt;p&gt;NOTE: If you intend to use Docker to run a local MongoDB instance in a container, you must first install Docker on your machine. Please consult the &lt;a href="https://docs.docker.com/install/"&gt;Docker installation documentation&lt;/a&gt; for your operating system.&lt;/p&gt;

&lt;p&gt;Since we're running MongoDB locally, we do not need to set any properties for our Spring Boot app. Spring Data MongoDB will automatically connect to the MongoDB instance exposed via the default port of 27017 on &lt;em&gt;localhost&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;To verify the application is working properly and is able to access the local MongoDB instance, execute the following commands from a terminal window:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;curl http://localhost:8080&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Alternatively, you can access &lt;code&gt;http://localhost:8080&lt;/code&gt; from a browser tab or window.&lt;/p&gt;

&lt;p&gt;NOTE: I use &lt;a href="https://httpie.io/"&gt;HTTPie&lt;/a&gt; instead of &lt;code&gt;curl&lt;/code&gt; and greatly prefer it. Commands seem more logical and output more readable. HTTPie leverages default values for many properties, such as the hostname: if not provided, it is assumed to be &lt;em&gt;localhost&lt;/em&gt;, a very reasonable assumption. In all of my examples, the &lt;code&gt;http&lt;/code&gt; command you see is HTTPie.&lt;/p&gt;

&lt;p&gt;The following should be displayed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HTTP/1.1 200 OK
Content-Type: application/json
transfer-encoding: chunked

[
    {
        "address": "123 N 45th St",
        "firstName": "Alpha",
        "id": "639900044b494a1a26d1c1fd",
        "lastName": "Bravo"
    },
    {
        "address": "1313 Mockingbird Lane",
        "firstName": "Charlie",
        "id": "639900044b494a1a26d1c1fe",
        "lastName": "Delta"
    }
]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To exercise the second endpoint and return the first user in the database, execute the following command:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;curl http://localhost:8080/oneuser&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You should see something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HTTP/1.1 200 OK
Content-Length: 98
Content-Type: application/json

{
    "address": "123 N 45th St",
    "firstName": "Alpha",
    "id": "639900044b494a1a26d1c1fd",
    "lastName": "Bravo"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To exercise the third endpoint and add a new user to the database, execute the following command:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;http POST :8080/newuser firstName=Echo lastName=Foxtrot address="21 Chester Place"&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You should see something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HTTP/1.1 200 OK
Content-Length: 102
Content-Type: application/json

{
    "address": "21 Chester Place",
    "firstName": "Echo",
    "id": "6399029c4b494a1a26d1c1ff",
    "lastName": "Foxtrot"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To double-check that all records are present in the database, you can once again access the first, summary endpoint via &lt;code&gt;http :8080&lt;/code&gt; or &lt;code&gt;curl http://localhost:8080&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  "Migrating" our app to Azure Cosmos DB for MongoDB
&lt;/h2&gt;

&lt;p&gt;It's almost embarrassing how easy it is to migrate our app to &lt;strong&gt;Azure Cosmos DB for MongoDB&lt;/strong&gt;. You'll see what I mean shortly, in the following sections.&lt;/p&gt;

&lt;h3&gt;
  
  
  Initialization and configuration
&lt;/h3&gt;

&lt;p&gt;I use two scripts (repository link at the bottom of this article) to prepare the Azure environment generally and Cosmos DB specifically for this project: one to initialize environment variables to be used, and one to create the &lt;em&gt;Azure Cosmos DB for MongoDB&lt;/em&gt; target based upon those variables and expose two variables Spring Data MongoDB expects for connections to databases at coordinates other than the defaults, localhost and port 27017.&lt;/p&gt;

&lt;p&gt;The script &lt;code&gt;CosmongoInitEnv.sh&lt;/code&gt; should be sourced by executing it in this manner from a terminal window:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;source ./CosmongoInitEnv.sh&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This assumes that the script resides in the current (.) directory, but if not, you must specify the full path to the shell script. Sourcing the script sets the environment variables contained therein and ensures they remain part of the environment after the script finishes, rather than spawning a different environment to run the script and having all changes made in that process disappear upon completion.&lt;/p&gt;

&lt;p&gt;To verify environment variables are set as expected, I like to check the environment using a command similar to the following:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;env | sort&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;or&lt;/p&gt;

&lt;p&gt;&lt;code&gt;env | grep COSMOSDB_MON&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;To create the Azure resources we will need for this project, and to expose the requisite variables for our app to locate our new cloud-based database, we then source the &lt;code&gt;CosmongoConfig.sh&lt;/code&gt; script as follows:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;source ./CosmongoConfig.sh&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Two things of note. First, this again assumes you are executing this script from the current (.) directory. Second, since we once again set environment variable values that we plan to use after the script finishes execution, we must source this script as well. Failure to do isn't catastrophic, but it will result in empty variables and will require manually executing the final three lines (the exports) from &lt;code&gt;CosmongoConfig.sh&lt;/code&gt; in the terminal in order to proceed.&lt;/p&gt;

&lt;p&gt;You can verify the required environment variables are now set using this or similar command:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;env | grep SPRING_DATA&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;These env vars are essential to the app we're about to create:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SPRING_DATA_MONGODB_URI&lt;/li&gt;
&lt;li&gt;SPRING_DATA_MONGODB_DATABASE&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If both variables' values are present, we can proceed to the next step and execute our application to begin using &lt;strong&gt;Azure Cosmos DB for MongoDB&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Changing the application
&lt;/h3&gt;

&lt;p&gt;Just kidding! The app is already ready to go. &lt;/p&gt;

&lt;p&gt;Yes, you read that correctly. Spring Data will automatically seek and incorporate the provided environment variables to connect to a database at coordinates other than the defaults. That's all that's required to migrate our app to &lt;strong&gt;Azure Cosmos DB for MongoDB&lt;/strong&gt;!&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo, take 2
&lt;/h2&gt;

&lt;p&gt;From the project directory, execute the following command to build and run the project:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;mvn spring-boot:run&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Exercising the endpoints produces results nearly identical to the ones before, except that the data is now stored in &lt;strong&gt;Azure Cosmos DB for MongoDB&lt;/strong&gt; (and of course, the database-assigned IDs are different).&lt;/p&gt;

&lt;p&gt;Retrieve all current users:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;» http :8080
HTTP/1.1 200 OK
Content-Type: application/json
transfer-encoding: chunked

[
    {
        "address": "123 N 45th St",
        "firstName": "Alpha",
        "id": "639bbb1db945f0487e9a778a",
        "lastName": "Bravo"
    },
    {
        "address": "1313 Mockingbird Lane",
        "firstName": "Charlie",
        "id": "639bbb1db945f0487e9a778b",
        "lastName": "Delta"
    }
]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Retrieve the first user in the database:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;» http :8080/oneuser                                                                
HTTP/1.1 200 OK
Content-Length: 98
Content-Type: application/json

{
    "address": "123 N 45th St",
    "firstName": "Alpha",
    "id": "639bbb1db945f0487e9a778a",
    "lastName": "Bravo"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add a new user to the database:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;» http POST :8080/newuser firstName=Echo lastName=Foxtrot address="21 Chester Place"
HTTP/1.1 200 OK
Content-Length: 102
Content-Type: application/json

{
    "address": "21 Chester Place",
    "firstName": "Echo",
    "id": "639bbb5cb945f0487e9a778c",
    "lastName": "Foxtrot"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Retrieve all users, verifying the new user we just added is present:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;» http :8080
HTTP/1.1 200 OK
Content-Type: application/json
transfer-encoding: chunked

[
    {
        "address": "123 N 45th St",
        "firstName": "Alpha",
        "id": "639bbb1db945f0487e9a778a",
        "lastName": "Bravo"
    },
    {
        "address": "1313 Mockingbird Lane",
        "firstName": "Charlie",
        "id": "639bbb1db945f0487e9a778b",
        "lastName": "Delta"
    },
    {
        "address": "21 Chester Place",
        "firstName": "Echo",
        "id": "639bbb5cb945f0487e9a778c",
        "lastName": "Foxtrot"
    }
]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Thanks to the developer-first mindset of the Spring Boot, Spring Data, and Cosmos DB development teams, upgrading your Spring Boot app from using MongoDB to &lt;strong&gt;Azure Cosmos DB for MongoDB&lt;/strong&gt; is as simple as changing a few environment variables and restarting your application. This enables you to go from concept to code to &lt;em&gt;planetary-scale production&lt;/em&gt; in a matter of minutes...or seconds, if you don't stop for coffee along the way!&lt;/p&gt;

&lt;p&gt;But don't worry, I won't tell your boss. Go ahead and grab that cup of java. You've earned it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/mkheck/cosmosdb-mon-scripts"&gt;Scripts to create Azure Cosmos DB for MongoDB database resources, instance and set app env vars&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/mkheck/cosmosdb-mon"&gt;Spring Boot project repository&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>springboot</category>
      <category>cosmosdb</category>
      <category>azure</category>
      <category>mongodb</category>
    </item>
    <item>
      <title>Cosmos DB for Spring Developers, Part I: Using Cosmos DB as a SQL Database</title>
      <dc:creator>Mark Heckler</dc:creator>
      <pubDate>Fri, 19 Aug 2022 00:31:04 +0000</pubDate>
      <link>https://dev.to/mkheck/cosmos-db-for-spring-developers-part-i-using-cosmos-db-as-a-sql-database-28me</link>
      <guid>https://dev.to/mkheck/cosmos-db-for-spring-developers-part-i-using-cosmos-db-as-a-sql-database-28me</guid>
      <description>&lt;p&gt;One of the key benefits of working with any Spring project, from Spring Framework to Spring Boot, Spring Data to Spring Cloud, is the developer-first focus each of those projects takes to delivering capabilities. This manifests in several ways, but the ways on which I want to focus with this series of articles are flexibility and scalability, especially with regard to database connectivity.&lt;/p&gt;

&lt;h2&gt;
  
  
  Delivering Value
&lt;/h2&gt;

&lt;p&gt;The vast majority of systems and the applications that constitute them provide most of their value via data, whether it be via storage/retrieval, manipulation/analysis, transport/exchange, or some combination of these or other operations. As developers, data permeates aspects of everything we do, and yet we often don't give data considerations the same level of attention that we do our apps and the logic contained therein.&lt;/p&gt;

&lt;p&gt;This may be due to the wealth of options we have - or assume, or even hope we have - at our disposal. But many times, it's largely due to the immediacy of the "demands of now": getting functionality developed, tested, and deployed, so we can move on to the next bit of needed functionality. There is no shortage of requirements after all; our customers and community are racing to provide their customers and community the functionality they need as well.&lt;/p&gt;

&lt;h2&gt;
  
  
  Spring Boot and Spring Data
&lt;/h2&gt;

&lt;p&gt;Spring Boot and Spring Data enable us as developers to "get it done" effectively. Both are force multipliers, levers and fulcrums that take the heavy lifting of boilerplate and ceremony, replacing repetitious patterns to which we developers have grown numb with software-driven opinions. If you have to do the same handful of steps as setup and teardown (for example) with little or no variation every time you perform a set of basic operations, chances are those surrounding steps can be incorporated into an opinion, implemented as a convention one follows within Spring Boot (instead of a manual configuration), and streamlined away from the top level of developer concern. There must be ways to tweak, configure, and disable these steps for edge cases of course, but this is what Spring Boot does as a matter of course.&lt;/p&gt;

&lt;p&gt;Spring Data adds convention over configuration and when used in a Spring Boot application, Boot's autoconfiguration makes accessing data via Spring Data straightforward via a variety of mechanisms: the Java Persistence API (JPA), Java Database Connectivity (JDBC), and various NoSQL database drivers for document, wide table, caching, in-memory, and graph databases to mention a few.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's talk about that Data
&lt;/h2&gt;

&lt;p&gt;What Spring Data &lt;em&gt;doesn't&lt;/em&gt; do is make determinations of scalability and suitability of various database implementations for a specific purpose or targeted use case. As technologists, that is for us to determine.&lt;/p&gt;

&lt;p&gt;Sometimes we accurately anticipate functional and non-functional requirements. Sometimes scope and scalability requirements remain reasonably close to expectations. And sometimes, when amazing things happen and our systems gain far greater traction and a much wider user base than we could have anticipated, we must scale &lt;em&gt;everything&lt;/em&gt; quickly and effectively, both our systems and the data on which they depend and use to provide such meaningful value. That's where Cosmos DB comes in.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cosmos DB
&lt;/h2&gt;

&lt;p&gt;&lt;a href="http://aka.ms/cosmosdb" rel="noopener noreferrer"&gt;Azure Cosmos DB&lt;/a&gt; is a planetary-scale data store that offers various ways and APIs to manage your critical data and provide access to it from anywhere at breathtaking speed. Spring Developers can leverage the power of Cosmos DB via Spring Boot starters, among other means, and those starters are built and maintained by Microsoft developers as natural complements to the opinions built into Spring Boot and Spring Data. Developing applications using Cosmos DB is easy and requires no adjustments to a developer's existing mental processes.&lt;/p&gt;

&lt;p&gt;One powerful facet of Cosmos DB is its chameleon-like quality of fulfilling developers' needs for various APIs and storage mechanisms. Cosmos DB can operate and be interacted with as a SQL database, a MongoDB(tm) document database, a Cassandra(tm) wide table database, and more: each one at planetary scale and speed, each one with little or no adjustment to your applications needed.&lt;/p&gt;

&lt;p&gt;For this article, I focus on the SQL engine and API for Cosmos DB, as leveraged from a Spring Boot application. Let's get started!&lt;/p&gt;

&lt;h2&gt;
  
  
  Initialization and configuration
&lt;/h2&gt;

&lt;p&gt;I use two scripts (repository link at the bottom of this article) to prepare the Azure environment generally and Cosmos DB specifically for this project: one to initialize environment variables to be used, and one to create the Azure Cosmos DB target based upon those variables.&lt;/p&gt;

&lt;p&gt;The script &lt;code&gt;CosmosInitEnv.sh&lt;/code&gt; should be sourced by executing it in this manner from a terminal window:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;source ./CosmosInitEnv.sh&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This assumes that the script resides in the current (.) directory, but if not, you must specify the full path to the shell script. Sourcing the script sets the environment variables contained therein and ensures they remain part of the environment after the script finishes, rather than spawning a different environment to run the script and having all changes made in that process disappear upon completion.&lt;/p&gt;

&lt;p&gt;To verify environment variables are set as expected, I like to check the environment using a command similar to the following:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;env | sort&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;or&lt;/p&gt;

&lt;p&gt;&lt;code&gt;env | grep COSMOSDB&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;To create the Azure resources we will need for this project, we then source the &lt;code&gt;CosmosConfig.sh&lt;/code&gt; script as follows:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;source ./CosmosConfig.sh&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Two things of note. First, this again assumes you are executing this script from the current (.) directory. Second, since we once again set environment variable values that we plan to use after the script finishes execution, we must source this script as well. Failure to do isn't catastrophic, but it will result in empty variables and will require manually executing the final two lines from &lt;code&gt;CosmosConfig.sh&lt;/code&gt; in the terminal in order to proceed.&lt;/p&gt;

&lt;p&gt;You can verify all environment variables are now set using this or similar command:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;env | grep COSMOSDB_SQL&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;These three env vars are essential to the app we're about to create:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;COSMOSDB_SQL_URL&lt;/li&gt;
&lt;li&gt;COSMOSDB_SQL_KEY&lt;/li&gt;
&lt;li&gt;COSMOSDB_SQL_NAME&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If all three variables' values are present, we can proceed to the next step and build an application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a Spring Boot app
&lt;/h2&gt;

&lt;p&gt;There are many ways to begin creating a Spring Boot application, but I prefer to begin with the &lt;a href="https://start.spring.io" rel="noopener noreferrer"&gt;Spring Initializr&lt;/a&gt;. Choose a meaningful artifact name, change the group name if you so choose, then select the dependencies as shown in the following screen capture.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create and open the project
&lt;/h3&gt;

&lt;p&gt;NOTE: All code is in a Github repository linked at the bottom of this article.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Favihkh2kb7l8n3n2t8zr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Favihkh2kb7l8n3n2t8zr.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I use only three dependencies for this example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reactive Web&lt;/strong&gt; includes all dependencies to create web apps using reactive streams, non-blocking and/or imperative APIs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Azure Cosmos DB&lt;/strong&gt; enables your app to access Cosmos DB using the SQL API&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lombok&lt;/strong&gt; is a boilerplate-reducing library, useful for domain classes, logging, and more&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Next, click the "Generate" button to generate the project structure and download the compressed project files (.zip). Once downloaded, go to the directory where the file was saved, decompress it, and open the project in the Integrated Development Environment (IDE) or text editor of your choice. I use IntelliJ IDEA and Visual Studio Code (VSCode) for nearly all my dev work, and for this article I'll open the project in IntelliJ by navigating (using the Mac Finder) into the expanded project directory and double-clicking on the Maven build file, &lt;em&gt;pom.xml&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Once the project is loaded, you can verify that the dependencies chosen from the Spring Initializr are present within the project by opening the &lt;em&gt;pom.xml&lt;/em&gt; file. There will be additional ones brought in for testing, etc., but these represent the three we selected before generating the project structure:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;org.springframework.boot&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;spring-boot-starter-webflux&amp;lt;/artifactId&amp;gt;
&amp;lt;/dependency&amp;gt;
&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;com.azure.spring&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;spring-cloud-azure-starter-data-cosmos&amp;lt;/artifactId&amp;gt;
&amp;lt;/dependency&amp;gt;
&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;org.projectlombok&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;lombok&amp;lt;/artifactId&amp;gt;
    &amp;lt;optional&amp;gt;true&amp;lt;/optional&amp;gt;
&amp;lt;/dependency&amp;gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  Set required properties
&lt;/h3&gt;

&lt;p&gt;Next, we must specify a few properties for our application to use in its initialization for Spring Boot's autoconfiguration to make the connection with the desired Cosmos DB instance. Open the &lt;em&gt;application.properties&lt;/em&gt; file in the project (under &lt;em&gt;src/main/resources&lt;/em&gt;) and add the following parameters:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

spring.cloud.azure.cosmos.endpoint=${COSMOSDB_SQL_URL}
spring.cloud.azure.cosmos.key=${COSMOSDB_SQL_KEY}
spring.cloud.azure.cosmos.database=${COSMOSDB_SQL_NAME}



&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;There are many ways for a Spring Boot app to ingest properties from its environment, but for this article, we'll simply use SpEL (Spring Expression Language) variables that map to underlying environment variables with values assigned by the scripts we sourced earlier from the command line.&lt;/p&gt;

&lt;h3&gt;
  
  
  Code the application
&lt;/h3&gt;

&lt;p&gt;I'll start with a streamlined example for this article and plan to build out in follow-on posts.&lt;/p&gt;

&lt;h4&gt;
  
  
  The domain
&lt;/h4&gt;

&lt;p&gt;First, I code a domain class. In this example, I create a &lt;code&gt;User&lt;/code&gt; class with an &lt;code&gt;id&lt;/code&gt;, &lt;code&gt;firstName&lt;/code&gt;, &lt;code&gt;lastName&lt;/code&gt;, and &lt;code&gt;address&lt;/code&gt; member variables. This class and its properties are annotated thusly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;@Container(containerName = "data")&lt;/code&gt; represents the container name within Cosmos DB&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@Data&lt;/code&gt; is a Lombok annotation that instructs the Lombok compile-time code generator to consider this a "data class" and generate accessors (getters) and mutators (setters) for each member variable, along with &lt;code&gt;equals()&lt;/code&gt;, &lt;code&gt;hashCode()&lt;/code&gt;, and &lt;code&gt;toString()&lt;/code&gt; methods&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@NoArgsConstructor&lt;/code&gt; is a Lombok annotation that instructs Lombok to generate a zero-argument constructor&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@RequiredArgsConstructor&lt;/code&gt; is a Lombok annotation that instructs Lombok to generate a constructor with a parameter for each "required" member variable, as designated by the &lt;code&gt;@NonNull&lt;/code&gt; member variable annotation&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@Id&lt;/code&gt; indicates which member variable corresponds to the underlying table's primary key&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@GeneratedValue&lt;/code&gt; specifies that the underlying data store will generate this value, i.e. it does not need to be provided by the application&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@NonNull&lt;/code&gt; is addressed under &lt;code&gt;@RequiredArgsConstructor&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@PartitionKey&lt;/code&gt; corresponds to the key used by Cosmos DB to partition data stored for this domain entity for access optimization&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;NOTE: In this code, I use lastName as the partition key. This is a terrible choice in most cases, as it typically doesn't provide a meaningful and/or reasonably even grouping of entities for optimizing access. I use it in this simplified example for expediency and would strongly encourage the reader to research partitions with specific domains/data in mind. Choose wisely.&lt;/p&gt;

&lt;h4&gt;
  
  
  The repository
&lt;/h4&gt;

&lt;p&gt;Spring Boot's autoconfiguration takes the power of Spring Data and amplifies it. By including a database driver in your classpath (including it as a dependency in your build file results in its inclusion for deployment) and &lt;code&gt;extend&lt;/code&gt;ing a Spring Data-derived interface in your application code, Spring Boot's autoconfiguration creates the beans necessary to provide a proxy to the desired underlying datastore. In our case, this is all we need to provide foundational database capabilities:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

interface UserRepository extends ReactiveCosmosRepository&amp;lt;User, String&amp;gt; {}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;In this single line of code, we are defining an interface called &lt;code&gt;UserRepository&lt;/code&gt; that will inherit and potentially extend the capabilities of the &lt;code&gt;ReactiveCosmosRepository&lt;/code&gt;, storing objects of type &lt;code&gt;User&lt;/code&gt; with identifiers (IDs) of type &lt;code&gt;String&lt;/code&gt;. Autoconfig does the rest.&lt;/p&gt;

&lt;p&gt;NOTE: We can do more, of course, defining custom query methods and more. But for this example, the provided functionality is sufficient.&lt;/p&gt;

&lt;h4&gt;
  
  
  The API
&lt;/h4&gt;

&lt;p&gt;Next, I define the Application Programming Interface (API) that provides the means and structure for external applications to interact with this service. Once again the focus is on simplicity, and I define only a single HTTP endpoint that provides a JSON listing (by default) of all Users upon request.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

@RestController
@AllArgsConstructor
class CosmosSqlController {
    private final UserRepository repo;

    @GetMapping
    Flux&amp;lt;User&amp;gt; getAllUsers() {
        return repo.findAll();
    }
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;@RestController&lt;/code&gt; annotation is provided by the Spring Framework and combines the functionality of &lt;code&gt;@Controller&lt;/code&gt;, to respond to requests, and &lt;code&gt;@ResponseBody&lt;/code&gt;, to make the resultant object(s) the response body itself rather than just providing access to the object(s) via a model variable, as is the typical MVC methodology.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;@AllArgsConstructor&lt;/code&gt; annotation instructs Lombok to create a constructor for the &lt;code&gt;CosmosSqlController&lt;/code&gt; class with a parameter (and thus required argument) for every member variable. Since the only member variable is a &lt;code&gt;UserRepository&lt;/code&gt;, Lombok generates a ctor with that single parameter.&lt;/p&gt;

&lt;p&gt;NOTE: &lt;code&gt;@AllArgsConstructor&lt;/code&gt; has the fortunate (or perilous) capability to update your constructor automatically if you simply add/remove member variables, so remember: With great power comes great responsibility. :)&lt;/p&gt;

&lt;h4&gt;
  
  
  The data
&lt;/h4&gt;

&lt;p&gt;To create a bit of sample data, I create a Spring bean using the &lt;code&gt;@Component&lt;/code&gt; annotation. This annotation instructs Spring Boot, upon application initialization, to create an instance of the annotated class and place that object (bean) into the application context, i.e. its Dependency Injection (DI) container. All beans in the DI container are managed by the Spring Boot application in terms of lifecycle and, of course, injection into other code as dependencies.&lt;/p&gt;

&lt;p&gt;Once the bean is constructed, the &lt;code&gt;loadData()&lt;/code&gt; method is executed automatically due to the &lt;code&gt;@PostConstruct&lt;/code&gt; annotation. In this application, the &lt;code&gt;loadData()&lt;/code&gt; method deletes all data in the underlying datastore, populates it with two sample &lt;code&gt;User&lt;/code&gt; records, returns all &lt;code&gt;User&lt;/code&gt; records now stored in the database, and logs them to the console for verification.&lt;/p&gt;

&lt;p&gt;NOTE: The Reactive Streams API and the implementation of it as provided by Spring WebFlux/Project Reactor is beyond the scope of this particular article. Please consult the appropriate documentation at the &lt;a href="https://docs.spring.io/spring-framework/docs/current/reference/html/web-reactive.html" rel="noopener noreferrer"&gt;'Web on Reactive Stack' Spring documentation site&lt;/a&gt;, any of several sessions I've delivered available on &lt;a href="https://www.youtube.com/c/MarkHeckler" rel="noopener noreferrer"&gt;my YouTube channel&lt;/a&gt;, or by visiting the &lt;a href="http://www.reactive-streams.org/" rel="noopener noreferrer"&gt;Reactive Streams&lt;/a&gt; and &lt;a href="https://projectreactor.io/" rel="noopener noreferrer"&gt;Project Reactor&lt;/a&gt; sites.&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

&lt;p&gt;@Slf4j&lt;br&gt;
@Component&lt;br&gt;
@AllArgsConstructor&lt;br&gt;
class DataLoader {&lt;br&gt;
    private final UserRepository repo;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@PostConstruct
void loadData() {
    repo.deleteAll()
            .thenMany(Flux.just(new User("Alpha", "Bravo", "123 N 4567th St"),
                    new User("Charlie", "Delta", "1313 Mockingbird Lane")))
            .flatMap(repo::save)
            .thenMany(repo.findAll())
            .subscribe(user -&amp;amp;gt; log.info(user.toString()));
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;}&lt;/p&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Demo&lt;br&gt;
&lt;/h2&gt;

&lt;p&gt;To access the environment variables set earlier as a result of sourcing the config script, we will need to build and run the Spring Boot application from the command line. From the project directory, execute the following command to build and run the project:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;mvn spring-boot:run&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;To verify the application is able to access the Cosmos DB instance and return the correct data, execute the following command from another terminal window:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;curl http://localhost:8080&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Alternatively, you can access &lt;code&gt;http://localhost:8080&lt;/code&gt; from a browser tab or window.&lt;/p&gt;

&lt;p&gt;The following should be displayed:&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

&lt;p&gt;» http :8080 &lt;br&gt;
HTTP/1.1 200 OK&lt;br&gt;
Content-Type: application/json&lt;br&gt;
transfer-encoding: chunked&lt;/p&gt;

&lt;p&gt;[&lt;br&gt;
    {&lt;br&gt;
        "address": "123 N 4567th St",&lt;br&gt;
        "firstName": "Alpha",&lt;br&gt;
        "id": "dcaca409-da66-4d53-9268-e6313b48bdd7",&lt;br&gt;
        "lastName": "Bravo"&lt;br&gt;
    },&lt;br&gt;
    {&lt;br&gt;
        "address": "1313 Mockingbird Lane",&lt;br&gt;
        "firstName": "Charlie",&lt;br&gt;
        "id": "14cf1b9b-f8c2-401b-affe-770fcaaacea3",&lt;br&gt;
        "lastName": "Delta"&lt;br&gt;
    }&lt;br&gt;
]&lt;/p&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Summary&lt;br&gt;
&lt;/h2&gt;

&lt;p&gt;One of the key benefits of using Spring Boot to develop Java (and Kotlin) applications is its developer-first focus. Spring Boot's flexibility and scalability, especially with regard to database connectivity, makes tapping into the power of a planetary-scale database such as Cosmos DB amazingly straightforward for developers. This lets devs concentrate on functionality that meets their stakeholders' needs and helps drive their organization forward, knowing that their data is safe, scalable, and accessible from anywhere Azure is reachable, e.g. planet Earth.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://youtu.be/4fSvyQw6luE" rel="noopener noreferrer"&gt;How to use Azure Cosmos DB from your Spring Boot App (video) - beginning to 44:30&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/mkheck/cosmosdb-sql-scripts" rel="noopener noreferrer"&gt;Scripts to create Cosmos DB SQL database resources, instance&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/mkheck/cosmosdb-sql" rel="noopener noreferrer"&gt;Spring Boot project repository&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>springboot</category>
      <category>cosmosdb</category>
      <category>azure</category>
      <category>spring</category>
    </item>
    <item>
      <title>A Concise Guide to Using Multiple Azure Storage Accounts from a Single Spring Boot Application</title>
      <dc:creator>Mark Heckler</dc:creator>
      <pubDate>Mon, 18 Jul 2022 20:36:16 +0000</pubDate>
      <link>https://dev.to/mkheck/a-concise-guide-to-using-multiple-azure-storage-accounts-from-a-single-spring-boot-application-5chf</link>
      <guid>https://dev.to/mkheck/a-concise-guide-to-using-multiple-azure-storage-accounts-from-a-single-spring-boot-application-5chf</guid>
      <description>&lt;p&gt;Spring projects in general are opinionated: 80-90% of use cases are handled "by default", and code is often much more concise than would be required otherwise due to Spring's preference of convention over configuration. These and other "opinions" can result in dramatically less code to write and maintain and as a result, more focused impact.&lt;/p&gt;

&lt;p&gt;In the vast majority of cases where Azure Storage is used from an application, there is no compelling advantage to using more than a single Azure storage account. But there are edge cases, and having the ability to use multiple Azure Storage accounts from a single app - even if we might only need that capability around 10% of the time - could provide an incredibly useful extension of our storage superpowers.&lt;/p&gt;

&lt;p&gt;This article is the result of a collaboration with Shi li Chen.&lt;/p&gt;

&lt;h2&gt;
  
  
  It's all about resources
&lt;/h2&gt;

&lt;p&gt;The Spring Framework defines the &lt;code&gt;Resource&lt;/code&gt; interface and provides several implementations built upon &lt;code&gt;Resource&lt;/code&gt; to facilitate developer access to low-level resources. In order to handle a particular kind of resource, two things are required:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;code&gt;Resource&lt;/code&gt; implementation&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;ResourcePatternResolver&lt;/code&gt; implementation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A Spring application evaluates resources in question using one or more registered resolvers. When the type of resource is identified, the appropriate &lt;code&gt;Resource&lt;/code&gt; implementation is used to access and/or manipulate the underlying resource.&lt;/p&gt;

&lt;p&gt;If the implementations built into Spring Framework don't fulfill your use case, it's fairly straightforward to add support for additional types of resources by defining your own implementations of &lt;code&gt;AbstractResource&lt;/code&gt; and &lt;code&gt;ResourcePatternResolver&lt;/code&gt; interfaces.&lt;/p&gt;

&lt;p&gt;This article will introduce the Spring Resource, review Spring Cloud Azure's implementation of Spring's &lt;code&gt;Resource&lt;/code&gt; (especially with regard to Azure Storage Account considerations and limitations), and consider how to expand said implementation to address those edge cases in which it would be useful to access multiple Azure Storage Accounts from a single Spring Boot application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting resourceful
&lt;/h2&gt;

&lt;p&gt;We've already mentioned that the Spring Framework defines several useful &lt;a href="https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#resources-implementations"&gt;Resource implementations&lt;/a&gt;. As of this writing, the default types are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;UrlResource&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ClassPathResource&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;FileSystemResource&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PathResource&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ServletContextResource&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;InputStreamResource&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ByteArrayResource&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As mentioned earlier, each resource will have a corresponding resource resolver.&lt;/p&gt;

&lt;p&gt;Enabling your Spring Boot application to use a custom &lt;code&gt;Resource&lt;/code&gt; requires the following actions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Implement the &lt;code&gt;Resource&lt;/code&gt; interface by extending &lt;code&gt;AbstractResource&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Implement the &lt;code&gt;ResourcePatternResolver&lt;/code&gt; interface to resolve the custom resource type&lt;/li&gt;
&lt;li&gt;Register the implementation of &lt;code&gt;ResourcePatternResolver&lt;/code&gt; as a bean&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;NOTE: Your resolver must be added to the default resource loader's resolver set using the &lt;code&gt;org.springframework.core.io.DefaultResourceLoader#addProtocolResolver&lt;/code&gt; method, but this code is present in &lt;code&gt;AbstractAzureStorageProtocolResolver&lt;/code&gt;; extending that class to create your implementation accomplishes this on your behalf unless you choose to override its &lt;code&gt;setResourceLoader&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;ResourceLoader&lt;/code&gt; attempts to resolve each &lt;code&gt;Resource&lt;/code&gt; by comparing its defined location/format with all registered protocol pattern resolvers until a non-null resource is returned. If no match is found, the &lt;code&gt;Resource&lt;/code&gt; will be evaluated against Spring's built-in pattern resolvers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Spring resources in Spring Cloud Azure
&lt;/h2&gt;

&lt;p&gt;Spring Cloud Azure provides two Spring resource and resource pattern resolver implementations. In this article, we only discuss the implementation of the Azure Storage Blob resource. You can examine the source code for Spring Cloud Azure &lt;code&gt;Resources&lt;/code&gt; at &lt;a href="https://github.com/Azure/azure-sdk-for-java/tree/spring-cloud-azure-starter-storage-blob_4.2.0/sdk/spring/spring-cloud-azure-core/src/main/java/com/azure/spring/cloud/core/resource"&gt;Spring Cloud Azure&lt;/a&gt; and related documentation at &lt;a href="https://microsoft.github.io/spring-cloud-azure/4.2.0/reference/html/index.html#resource-handling"&gt;Resource Handling&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;NOTE: We use &lt;a href="https://mvnrepository.com/artifact/com.azure.spring/spring-cloud-azure-starter-storage-blob/4.2.0"&gt;Spring Cloud Azure Starter Storage Blob version 4.2.0&lt;/a&gt; for analysis and experiments.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation of &lt;code&gt;AbstractResource&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The abstract implementation &lt;code&gt;AzureStorageResource&lt;/code&gt; for Spring Cloud Azure primarily defines the format of the Azure storage resource protocol and accommodates the unique attributes of the Azure Storage Account service, e.g. the container name and file name. It is important to note that &lt;code&gt;AzureStorageResource&lt;/code&gt; is decoupled from the Azure Storage SDK.&lt;/p&gt;

&lt;p&gt;The Spring Framework interface &lt;code&gt;WritableResource&lt;/code&gt; represents the underlying API we build upon to read from and write to the Azure Storage resource.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;abstract class AzureStorageResource extends AbstractResource implements WritableResource {

    private boolean isAzureStorageResource(@NonNull String location) {
        ......
    }

    String getContainerName(String location) {
        ......
    }

    String getContentType(String location) {
        ......
    }

    String getFilename(String location) {
        ......
    }

    abstract StorageType getStorageType();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;StorageBlobResource&lt;/code&gt; is Spring Cloud Azure Storage Blob's implementation of the abstract class &lt;code&gt;AbstractResource&lt;/code&gt;.&lt;br&gt;
We can see &lt;code&gt;StorageBlobResource&lt;/code&gt; uses the &lt;code&gt;BlobServiceClient&lt;/code&gt; from the Azure Storage Blob SDK to implement all abstract methods, relying on the service client to interact with the Azure Storage Blob service.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public final class StorageBlobResource extends AzureStorageResource {
    private final BlobServiceClient blobServiceClient;
    private final BlobContainerClient blobContainerClient;
    private final BlockBlobClient blockBlobClient;

    public StorageBlobResource(BlobServiceClient blobServiceClient, String location, Boolean autoCreateFiles,
                               String snapshot, String versionId, String contentType) {
        ......
        this.blobContainerClient = blobServiceClient.getBlobContainerClient(getContainerName(location));
        BlobClient blobClient = blobContainerClient.getBlobClient(getFilename(location));
        this.blockBlobClient = blobClient.getBlockBlobClient();
    }

    @Override
    public OutputStream getOutputStream() throws IOException {
        try {
            ......
            return this.blockBlobClient.getBlobOutputStream(options);
        } catch (BlobStorageException e) {
            throw new IOException(MSG_FAIL_OPEN_OUTPUT, e);
        }
    }

    ......

    @Override
    StorageType getStorageType() {
        return StorageType.BLOB;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Implementation of &lt;code&gt;ResourcePatternResolver&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Spring Cloud Azure provides an abstract implementation &lt;code&gt;AbstractAzureStorageProtocolResolver&lt;/code&gt;. This class incorporates general processing of the Azure storage resource protocol, exposes specific capabilities of the Azure Storage Account service, and adds the requisite logic to the default resource loader. Like &lt;code&gt;AzureStorageResource&lt;/code&gt;, the &lt;code&gt;AbstractAzureStorageProtocolResolver&lt;/code&gt; is also not coupled to the Azure Storage SDK.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public abstract class AbstractAzureStorageProtocolResolver implements ProtocolResolver, ResourcePatternResolver,
    ResourceLoaderAware, BeanFactoryPostProcessor {

    protected final AntPathMatcher matcher = new AntPathMatcher();

    protected abstract StorageType getStorageType();

    protected abstract Resource getStorageResource(String location, Boolean autoCreate);

    protected ConfigurableListableBeanFactory beanFactory;

    protected abstract Stream&amp;lt;StorageContainerItem&amp;gt; listStorageContainers(String containerPrefix);

    protected abstract StorageContainerClient getStorageContainerClient(String name);

    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        if (resourceLoader instanceof DefaultResourceLoader) {
            ((DefaultResourceLoader) resourceLoader).addProtocolResolver(this);
        } else {
            LOGGER.warn("Custom Protocol using azure-{}:// prefix will not be enabled.", getStorageType().getType());
        }
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }

    @Override
    public Resource resolve(String location, ResourceLoader resourceLoader) {
        if (AzureStorageUtils.isAzureStorageResource(location, getStorageType())) {
            return getResource(location);
        }
        return null;
    }

    @Override
    public Resource[] getResources(String pattern) throws IOException {
        Resource[] resources = null;

        if (AzureStorageUtils.isAzureStorageResource(pattern, getStorageType())) {
            if (matcher.isPattern(AzureStorageUtils.stripProtocol(pattern, getStorageType()))) {
                String containerPattern = AzureStorageUtils.getContainerName(pattern, getStorageType());
                String filePattern = AzureStorageUtils.getFilename(pattern, getStorageType());
                resources = resolveResources(containerPattern, filePattern);
            } else {
                return new Resource[] { getResource(pattern) };
            }
        }
        if (null == resources) {
            throw new IOException("Resources not found at " + pattern);
        }
        return resources;
    }

    @Override
    public Resource getResource(String location) {
        Resource resource = null;

        if (AzureStorageUtils.isAzureStorageResource(location, getStorageType())) {
            resource = getStorageResource(location, true);
        }

        if (null == resource) {
            throw new IllegalArgumentException("Resource not found at " + location);
        }
        return resource;
    }

    /**
     * Storage container item.
     */
    protected static class StorageContainerItem {
        private final String name;
        ......
    }

    protected static class StorageItem {

        private final String container;
        private final String name;
        private final StorageType storageType;
        ......
    }

    protected interface StorageContainerClient {

        ......
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The resource resolver &lt;code&gt;AzureStorageBlobProtocolResolver&lt;/code&gt; is Spring Cloud Azure Storage Blob's implementation of &lt;code&gt;ResourcePatternResolver&lt;/code&gt;. It encapsulates resources according to the location or storage item pattern based on &lt;code&gt;BlobServiceClient&lt;/code&gt; and returns the associated &lt;code&gt;StorageBlobResource&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public final class AzureStorageBlobProtocolResolver extends AbstractAzureStorageProtocolResolver {

    private BlobServiceClient blobServiceClient;
    @Override
    protected StorageType getStorageType() {
        return StorageType.BLOB;
    }

    @Override
    protected Resource getStorageResource(String location, Boolean autoCreate) {
        return new StorageBlobResource(getBlobServiceClient(), location, autoCreate);
    }

    private BlobServiceClient getBlobServiceClient() {
        if (blobServiceClient == null) {
            blobServiceClient = beanFactory.getBean(BlobServiceClient.class);
        }
        return blobServiceClient;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Opinions
&lt;/h2&gt;

&lt;p&gt;As mentioned at the beginning of this post, the default capabilities fulfill the requirements admirably in the vast majority of circumstances. But in accordance with the Spring ethos, Spring Cloud Azure Starter Storage Blob was designed to seamlessly address 80-90% of use cases "out of the box", while still allowing for remaining (edge) cases with some extra effort.&lt;/p&gt;

&lt;p&gt;As written, the storage blob resource supports multiple container operations using the same storage account. The salient point is that the blob paths under different containers can be properly resolved into &lt;code&gt;StorageBlobResource&lt;/code&gt; objects. Combining the earlier code for &lt;code&gt;StorageBlobResource&lt;/code&gt;, the blob resource must hold a blob service client, and if &lt;code&gt;blobServiceClient.getBlobContainerClient(getContainerName(location))&lt;/code&gt; successfully returns a &lt;code&gt;BlobServiceClient&lt;/code&gt;, the blob resource can be resolved and retrieved.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;BlobServiceClient&lt;/code&gt; bean represents an Azure Storage Account in the Azure Storage Blob SDK, meaning that the current implementation does not support simultaneous availability using multiple Azure Storage Accounts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Developing an extended version of Spring Cloud Azure Starter Storage Blob
&lt;/h2&gt;

&lt;p&gt;For those rare cases in which it might be useful to simultaneously access multiple Azure Storage accounts from the same application, there is a way to make that happen. To demonstrate this capability, let's create a new library called &lt;code&gt;spring-cloud-azure-starter-storage-blob-extend&lt;/code&gt;. The only external dependency for this new library is the existing &lt;code&gt;spring-cloud-azure-starter-storage-blob&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Extend the Storage Blob properties
&lt;/h2&gt;

&lt;p&gt;While the primary goal is to support multiple storage accounts, a secondary design goal is to use a similar structure to &lt;code&gt;AzureStorageBlobProperties&lt;/code&gt; in order to minimize the learning curve and to retain Spring Cloud Azure 4.0's out of the box authentication features.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class ExtendAzureStorageBlobsProperties {

    public static final String PREFIX = "spring.cloud.azure.storage.blobs";

    private boolean enabled = true;

    private final List&amp;lt;AzureStorageBlobProperties&amp;gt; configurations = new ArrayList&amp;lt;&amp;gt;();

    public boolean isEnabled() {
        return enabled;
    }

    public void setEnabled(boolean enabled) {
        this.enabled = enabled;
    }

    public List&amp;lt;AzureStorageBlobProperties&amp;gt; getConfigurations() {
        return configurations;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Dynamically register Storage Blob beans
&lt;/h2&gt;

&lt;p&gt;Since there will be multiple Storage Account configurations, we must name the beans corresponding to each storage account. The cleanest approach is to simply use the account name as the bean name.&lt;/p&gt;

&lt;p&gt;Now, let's dynamically register these beans with the Spring context.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(value = { "spring.cloud.azure.storage.blobs.enabled"}, havingValue = "true")
public class ExtendStorageBlobsAutoConfiguration implements BeanDefinitionRegistryPostProcessor, EnvironmentAware {

    private Environment environment;

    public static final String EXTEND_STORAGE_BLOB_PROPERTIES_BEAN_NAME = "extendAzureStorageBlobsProperties";

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        AzureGlobalProperties azureGlobalProperties =
            Binder.get(environment)
                  .bind(AzureGlobalProperties.PREFIX, AzureGlobalProperties.class)
                  .orElse(new AzureGlobalProperties());
        ExtendAzureStorageBlobsProperties blobsProperties =
            Binder.get(environment)
                  .bind(ExtendAzureStorageBlobsProperties.PREFIX, ExtendAzureStorageBlobsProperties.class)
                  .orElseThrow(() -&amp;gt; new IllegalArgumentException("Can not bind the azure storage blobs properties."));
        // merge properties
        for (AzureStorageBlobProperties azureStorageBlobProperties : blobsProperties.getConfigurations()) {
            AzureStorageBlobProperties transProperties = new AzureStorageBlobProperties();
            AzureGlobalPropertiesUtils.loadProperties(azureGlobalProperties, transProperties);
            copyAzureCommonPropertiesIgnoreTargetNull(transProperties, azureStorageBlobProperties);
        }

        DefaultListableBeanFactory factory = (DefaultListableBeanFactory) beanFactory;
        registryBeanExtendAzureStorageBlobsProperties(factory, blobsProperties);
        blobsProperties.getConfigurations().forEach(blobProperties -&amp;gt; registryBlobBeans(factory, blobProperties));
    }

    private void registryBeanExtendAzureStorageBlobsProperties(DefaultListableBeanFactory beanFactory,
                                                               ExtendAzureStorageBlobsProperties blobsProperties) {
        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(ExtendAzureStorageBlobsProperties.class,
            () -&amp;gt; blobsProperties);
        AbstractBeanDefinition rawBeanDefinition = beanDefinitionBuilder.getRawBeanDefinition();
        beanFactory.registerBeanDefinition(EXTEND_STORAGE_BLOB_PROPERTIES_BEAN_NAME, rawBeanDefinition);
    }

    private void registryBlobBeans(DefaultListableBeanFactory beanFactory, AzureStorageBlobProperties blobProperties) {
        String accountName = getStorageAccountName(blobProperties);
        Assert.hasText(accountName, "accountName can not be null or empty.");
        registryBeanStaticConnectionStringProvider(beanFactory, blobProperties, accountName);
        registryBeanBlobServiceClientBuilderFactory(beanFactory, blobProperties, accountName);
        registryBeanBlobServiceClientBuilder(beanFactory, accountName);
        registryBeanBlobServiceClient(beanFactory, accountName);
        registryBeanBlobContainerClient(beanFactory, blobProperties, accountName);
        registryBeanBlobClient(beanFactory, blobProperties, accountName);
    }

    private void registryBeanBlobServiceClientBuilder(DefaultListableBeanFactory beanFactory,
                                                      String accountName) {
        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(BlobServiceClientBuilder.class,
            () -&amp;gt; {
                BlobServiceClientBuilderFactory builderFactory =
                    beanFactory.getBean(accountName + BlobServiceClientBuilderFactory.class.getSimpleName(),
                        BlobServiceClientBuilderFactory.class);
                return builderFactory.build();
            });
        AbstractBeanDefinition rawBeanDefinition = beanDefinitionBuilder.getRawBeanDefinition();
        beanFactory.registerBeanDefinition(
            accountName + BlobServiceClientBuilder.class.getSimpleName(), rawBeanDefinition);
    }

    ......

    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Extend the &lt;code&gt;AzureStorageBlobProtocolResolver&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The next task is to make any container resolvable by the same resource pattern resolver. Specifying a storage blob resource location such as &lt;strong&gt;azure-blob-accountname://containername/test.txt&lt;/strong&gt;, the resolver will use that to locate the appropriate &lt;code&gt;BlobServiceClient&lt;/code&gt; bean by Azure Storage Account name and return the storage resource.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class ExtendAzureStorageBlobProtocolResolver extends ExtendAbstractAzureStorageProtocolResolver {
    private final Map&amp;lt;String, BlobServiceClient&amp;gt; blobServiceClientMap = new HashMap&amp;lt;&amp;gt;();

    @Override
    protected Resource getStorageResource(String location, Boolean autoCreate) {
        return new ExtendStorageBlobResource(getBlobServiceClient(location), location, autoCreate);
    }

    private BlobServiceClient getBlobServiceClient(String locationPrefix) {
        String storageAccount = ExtendAzureStorageUtils.getStorageAccountName(locationPrefix, getStorageType());
        Assert.notNull(storageAccount, "storageAccount can not be null.");
        String accountKey = storageAccount.toLowerCase(Locale.ROOT);
        if (blobServiceClientMap.containsKey(accountKey)) {
            return blobServiceClientMap.get(accountKey);
        }

        BlobServiceClient blobServiceClient = beanFactory.getBean(
            accountKey + BlobServiceClient.class.getSimpleName(), BlobServiceClient.class);
        Assert.notNull(blobServiceClient, "blobServiceClient can not be null.");
        blobServiceClientMap.put(accountKey, blobServiceClient);
        return blobServiceClient;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Again, you need to add the bean &lt;code&gt;ExtendAzureStorageBlobProtocolResolver&lt;/code&gt; to the Spring context.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing the Spring Cloud Azure Starter Storage Blob Extend
&lt;/h2&gt;

&lt;p&gt;You can use &lt;a href="https://start.spring.io/"&gt;start.spring.io&lt;/a&gt; to generate a Spring Boot 2.6.7 or greater project with Azure Storage support (or build on &lt;a href="https://github.com/Azure-Samples/azure-spring-boot-samples/tree/spring-cloud-azure_v4.2.0/storage/spring-cloud-azure-starter-storage-blob/storage-blob-sample"&gt;this storage blob sample&lt;/a&gt; if you prefer).&lt;/p&gt;

&lt;p&gt;Add the extending starter dependency to the &lt;em&gt;pom.xml&lt;/em&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;dependency&amp;gt;
  &amp;lt;groupId&amp;gt;com.azure.spring.extend&amp;lt;/groupId&amp;gt;
  &amp;lt;artifactId&amp;gt;spring-cloud-azure-starter-storage-blob-extend&amp;lt;/artifactId&amp;gt;
  &amp;lt;version&amp;gt;1.0-SNAPSHOT&amp;lt;/version&amp;gt;
&amp;lt;/dependency&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Delete the &lt;em&gt;src/main/resources/application.properties&lt;/em&gt; file or add the following configuration file &lt;em&gt;application-extend.yml&lt;/em&gt;, which enables multiple storage account usage:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;application-extend.yml&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;spring:
  cloud:
    azure:
      storage:
        blob:
          enabled: false
        blobs:
          enabled: true
          configurations:
            - account-name: ${FIRST_ACCOUNT}
              container-name: ${FIRST_CONTAINER}
              account-key: ${ACCOUNT_KEY_OF_FIRST_ACCOUNT}
            - account-name: ${SECOND_ACCOUNT}
              container-name: ${SECOND_CONTAINER}
              account-key: ${ACCOUNT_KEY_OF_SECOND_ACCOUNT}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;NOTE: You must provide values for the environment variables above (listed in all capital letters) with active Azure Storage Account resource information.&lt;/p&gt;

&lt;p&gt;Add class &lt;code&gt;com.azure.spring.extend.sample.storage.resource.extend.SampleDataInitializer&lt;/code&gt; with the following body:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Profile("extend")
@Component
public class SampleDataInitializer implements CommandLineRunner {
    final static Logger logger = LoggerFactory.getLogger(SampleDataInitializer.class);

    private final ConfigurableEnvironment env;

    private final ExtendAzureStorageBlobProtocolResolver resolver;
    private final ExtendAzureStorageBlobsProperties properties;

    public SampleDataInitializer(ConfigurableEnvironment env, ExtendAzureStorageBlobProtocolResolver resolver,
                                 ExtendAzureStorageBlobsProperties properties) {
        this.env = env;
        this.resolver = resolver;
        this.properties = properties;
    }

    /**
     * This is used to initialize some data for each Azure Storage Account Blob container.
     */
    @Override
    public void run(String... args) {
        properties.getConfigurations().forEach(this::writeDataByStorageAccount);
    }

    private void writeDataByStorageAccount(AzureStorageBlobProperties blobProperties) {
        String containerName = blobProperties.getContainerName();
        if (!StringUtils.hasText(containerName) || blobProperties.getAccountName() == null) {
            return;
        }

        String accountName = getStorageAccountName(blobProperties);
        logger.info("Begin to initialize the {} container of the {} account", containerName, accountName);
        long currentTimeMillis = System.currentTimeMillis();
        String fileName = "fileName-" + currentTimeMillis;
        String data = "data" + currentTimeMillis;
        Resource storageBlobResource = resolver.getResource("azure-blob-" + accountName + "://" + containerName +"/" + fileName + ".txt");
        try (OutputStream os = ((WritableResource) storageBlobResource).getOutputStream()) {
            os.write(data.getBytes());
            logger.info("Write data to container={}, fileName={}.txt", containerName, fileName);
        } catch (IOException e) {
            logger.error("Write data exception", e);
        }
        logger.info("End to initialize the {} container of the {} account", containerName, accountName);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the sample with following Maven command:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;mvn clean spring-boot:run -Dspring-boot.run.profiles=extend&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Finally, verify the expected outcome. Your console should display the following output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;c.a.s.e.s.s.r.e.SampleDataInitializer    : Begin to initialize the container first of the account firstaccount.
c.a.s.e.s.s.r.e.SampleDataInitializer    : Write data to container=first, fileName=fileName-1656641340271.txt
c.a.s.e.s.s.r.e.SampleDataInitializer    : End to initialize the container first of the account firstaccount.
c.a.s.e.s.s.r.e.SampleDataInitializer    : Begin to initialize the container second of the account secondaccount.
c.a.s.e.s.s.r.e.SampleDataInitializer    : Write data to container=second, fileName=fileName-1656641343572.txt
c.a.s.e.s.s.r.e.SampleDataInitializer    : End to initialize the container second of the account secondaccount.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All sample project code is published at the repository &lt;a href="https://github.com/moarychan/spring-cloud-azure-starter-storage-blob-extend-sample/tree/main/spring-cloud-azure-starter-storage-blob-extend-sample"&gt;spring-cloud-azure-starter-storage-blob-extend-sample&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Within this extended application, it's still possible to revert to the original, single storage account usage of Spring Cloud Azure Starter Storage Blob by adding the following configuration file &lt;em&gt;application-current.yml&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;spring:
  cloud:
    azure:
      storage:
        blob:
          account-name: ${FIRST_ACCOUNT}
          container-name: ${FIRST_CONTAINER}
          account-key: ${ACCOUNT_KEY_OF_FIRST_ACCOUNT}
current:
  second-container: ${SECOND_CONTAINER}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;NOTE: You must set or replace the listed environment variable assigned values with active Azure Storage Account resource information.&lt;/p&gt;

&lt;p&gt;Run the sample with following Maven command:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;mvn clean spring-boot:run -Dspring-boot.run.profiles=current&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;To verify correct operation using a single storage account, compare terminal output with that listed here:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;c.a.s.e.s.s.r.c.SampleDataInitializer    : StorageApplication data initialization of 'first-container' begin ...
c.a.s.e.s.s.r.c.SampleDataInitializer    : Write data to container=first-container, fileName=fileName1656641162614.txt
c.a.s.e.s.s.r.c.SampleDataInitializer    : StorageApplication data initialization of 'first-container' end ...
c.a.s.e.s.s.r.c.SampleDataInitializer    : StorageApplication data initialization of 'second-container' begin ...
c.a.s.e.s.s.r.c.SampleDataInitializer    : Write data to container=second-container, fileName=fileName1656641165411.txt
c.a.s.e.s.s.r.c.SampleDataInitializer    : StorageApplication data initialization of 'second-container' end ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Implementing a specific resource type and corresponding pattern resolver is relatively simple, largely thanks to clear documentation, the many built-in implementations, common usage within the Spring technology stack.&lt;/p&gt;

&lt;p&gt;One point that warrants attention is the protocol definition for the resource, e.g. the Azure Storage Blob Resource. We must note whether we are using &lt;strong&gt;azure-blob://&lt;/strong&gt; or &lt;strong&gt;azure-blob-[account-name]://&lt;/strong&gt; and plan app capabilities accordingly. Additionally, since the identifier of a network resource must be uniquely identifiable, the latter location format may result in a much longer name and also exposes the name of the storage account. These tradeoffs need to be evaluated in light of requirements and risk profile.&lt;/p&gt;

&lt;h2&gt;
  
  
  References &amp;amp; Useful Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#resources"&gt;Latest Spring Resources documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/moarychan/spring-cloud-azure-starter-storage-blob-extend-sample"&gt;Extended starter for Spring Cloud Azure Starter Storage Blob and sample on GitHub&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>azure</category>
      <category>springboot</category>
      <category>azurestorage</category>
      <category>java</category>
    </item>
  </channel>
</rss>
