Sitemap

Why and How we made Spring AI optional in Redis OM Spring

6 min readApr 6, 2025

--

When building a Spring library, you might want to include optional dependencies. These are features that can enhance your library but aren’t essential for it to work. This approach ensures that developers using your library don’t have to include components they don’t need.

In the case of Redis OM Spring, a library that extends Spring Data Redis, we faced a specific challenge: we needed to avoid including a dependency that hadn’t yet reached a stable version.

Redis OM Spring is widely used by customers and companies, many of whom follow strict rules about which dependencies they can use. Since one of the dependencies we relied on wasn’t in a stable release, some users couldn’t use our latest updates and features. Although they could manually exclude the dependencies, this wasn’t a solution because Redis OM Spring would break without them.

To continue supporting all users while keeping the library functional, we decided to make this dependency optional.

The Dependency in Question

Redis has evolved over the past 15 years into a complete database with a wide variety of capabilities. However, Spring Data Redis, the library commonly used to connect to Redis, doesn’t support many of these advanced features.

Some examples are:

  • Support for JSON (in addition to HASH).
  • Probabilistic data structures like Bloom Filters.
  • The Redis Query Engine for fast full text search.
  • Vector Similarity Search, useful for AI applications.

Redis OM Spring was created to bridge this gap by adding support for these advanced features to the Spring Framework Ecosystem.

The most recent feature we’ve added is support for Vector Similarity Search, which integrates seamlessly with the new Spring library, Spring AI.

Our most recent addition was Vector Similarity Search, which works with another library called Spring AI. Spring AI is a newer library that still hasn’t reached a stable version. Because of this some of our customers and users, unable to use a dependency that hasn’t reached a stable version, were also not able to upgrade their version of Redis OM Spring. This made Spring AI the dependency we wanted to make optional. At least, temporarily.

Making Dependencies Optional

From a Maven perspective, making a dependency optional is straightforward. You just add the <optional>true</optional> tag in your pom.xml file:

<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-openai</artifactId>
<optional>true</optional>
</dependency>

Alternatively, users can manually exclude the dependency when they include Redis OM Spring:

<dependency>
<groupId>com.redis.om</groupId>
<artifactId>redis-om-spring</artifactId>
<version>0.9.7</version>
<exclusions>
<exclusion>
<groupId>org.springframework.ai</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>

However, simply making the dependency optional isn’t enough. If certain beans in your library depend on the optional dependency, the library could still break if that dependency isn’t included. To fix this, we needed to make sure Redis OM Spring could function properly without those beans.

Running Redis OM Spring without Spring AI dependencies would throw a ClassNotFoundException

How We Made Redis OM Spring Work Without Optional Dependencies

We decided to make all AI-related features in Redis OM Spring optional. These features, including Vector Similarity Search, depend on both Spring AI and DJL (Deep Java Library), which we use to generate embeddings for the data.

Step 1: Identify AI-Dependent Beans
Most beans in Redis OM Spring are defined in a single configuration file called RedisModulesConfiguration.java. By reviewing this file, we identified which beans relied on AI features. For example:

@ConditionalOnMissingBean
@Bean
public OpenAiEmbeddingModel openAITextVectorizer(RedisOMProperties properties,
@Value("${spring.ai.openai.api-key:}") String apiKey) {
[...]
}

Next, we added a condition to ensure these beans are only created if AI features are enabled. We used the @ConditionalOnProperty annotation for this:

@ConditionalOnProperty(name = "redis.om.spring.ai.enabled", havingValue = "true")
@Bean
public OpenAiEmbeddingModel openAITextVectorizer(RedisOMProperties properties,
@Value("${spring.ai.openai.api-key:}") String apiKey) {
[...]
}

This ensures the AI-related beans are only created when the redis.om.spring.ai.enabled property is explicitly set to true.

Step 2: Split Properties Classes
The AI-related dependencies were also included in the RedisOMProperties class. Since this class is always loaded, it would throw a ClassNotFoundException if the dependencies weren’t available. To solve this, we split the properties into two classes:

• RedisOMProperties (for general properties)

• RedisOMAIProperties (for AI-related properties)

The new RedisOMAIProperties class is only loaded when the redis.om.spring.ai.enabled property is set to true.

Step 3: Split Configuration Classes
We then split the RedisModulesConfiguration class into two:

• RedisModulesConfiguration (for non-AI features)

• RedisAiConfiguration (for AI features)

The RedisAiConfiguration class is also conditioned to the redis.om.spring.ai.enabled property:

@ConditionalOnProperty(name = "redis.om.spring.ai.enabled", havingValue = "true")
@Configuration
@EnableConfigurationProperties(RedisOMAIProperties.class)
public class RedisAiConfiguration {
// AI-related beans
}

Step 4: Update Spring Factories
When building a Spring Boot application, we typically have an Application class with a main method that acts as the entry point for the application. This class is responsible for starting the Spring Boot application context. For example:

@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}

However, when building a Spring Boot library, like Redis OM Spring, you don’t have an Application class because the library itself isn’t an application, it’s meant to be used as part of other applications. Instead, we usually develop our own auto-configuration. These auto-configuration classes can be bundled in external jars and still be picked-up by Spring Boot.

In Redis OM Spring, the META-INF/spring.factories file serves this purpose by registering configuration classes like RedisModulesConfiguration and RedisAiConfiguration. These classes are then loaded automatically by Spring Boot when a user adds Redis OM Spring as a dependency in their project.

At startup, Spring Boot checks for the presence of a META-INF/spring.factories file within your published jar. The file should list your configuration classes under theEnableAutoConfiguration key. In the case of Redis OM Spring, there was only one configuration class listed:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.redis.om.spring.RedisModulesConfiguration

Now, com.redis.om.spring.RedisAiConfiguration, also needed to be included:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.redis.om.spring.RedisModulesConfiguration
com.redis.om.spring.RedisAiConfiguration

This addition allows the Spring Boot application that is using our library to also detect our new Configuration class in the auto-configuration process.

Ensuring Everything Works

Testing a library with optional dependencies can be a bit tricky. This is because the library’s main pom.xml file typically includes all the dependencies needed for its features, making it hard to verify if the library works without certain dependencies.

To solve this, we used the separate modules we already had for demonstrating Redis OM Spring. These demo modules served as isolated environments for testing, allowing us to see how the library behaved with and without the optional dependencies.

Redis OM Spring has five demo modules:

  • roms-documents
  • roms-hashes
  • roms-permits
  • roms-vss

The first three demos didn’t rely on the optional AI dependencies, while the last one (roms-vss) specifically required them. This setup made testing straightforward:

• Running the first three demos confirmed that Redis OM Spring worked without the AI dependencies.

• Running the roms-vss demo, however, failed because the required AI dependencies (like Spring AI) were missing.

To ensure the AI-related features worked, we updated the roms-vss demo by explicitly adding the required dependencies (Spring AI and DJL) to its pom.xml file. After doing this, we ran the demo again, and this time, it worked perfectly.

<!-- Spring AI and DJL dependencies are optional and must be manually added in order to benefit from VSS features -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-openai</artifactId>
</dependency>
<dependency>
<groupId>ai.djl.spring</groupId>
<artifactId>djl-spring-boot-starter-autoconfigure</artifactId>
<version>${djl.starter.version}</version>
</dependency>
[...]

By using the demo modules, we could verify that:

1. Redis OM Spring worked correctly without the optional dependencies (validated by the first three demos).

2. AI-related features worked properly when the necessary dependencies were explicitly added (validated by the updated roms-vss demo).

Besides that, when running the following command:

mvn dependency:tree

We were also able to validate that related Spring AI dependencies were not included in the dependency tree of our project.

But there was something else

Unfortunately, when being tested by our customer, this approach didn't entirely work. They were still getting an error related to the resolution of the Spring AI BOM.

[ERROR] Failed to read artifact descriptor for com.redis.om:redis-om-spring:jar:0.9.8
[ERROR] [server] Maven transfer artifact problem: org.springframework.ai:spring-ai-bom:pom:1.0.0-M2
Cannot resolve org.springframework.ai:spring-ai-bom:1.0.0-M2

In our tests, we hadn't realized Spring AI BOM's POM file was still being downloaded. Dependencies weren't downloaded and nothing was showing up in the dependency tree, as expected, but Maven was still trying to download and resolve its POM file.

The only way we could actually see this was happening was by checking what was inside of our .m2 folder:

Unfortunately, we couldn't find a way to make dependency management's optional. Including the <optional>true</optional> and the <scope>import</scope>tags didn't work. So the easiest way to overcome this issue was by removing the Spring AI BOM altogether and manually managing the related versions.

This temporary solution allowed us to ensure that Redis OM Spring functions both with and without the optional dependencies, keeping the library flexible for all of our users and customers.

Stay Curious!

--

--

Raphael De Lio
Raphael De Lio

Written by Raphael De Lio

Software Engineer | Developer Advocate | International Conference Speaker | Tech Content Creator | Working @ Redis | https://linktr.ee/raphaeldelio

No responses yet