Master Maven Dependency Optional
optional dependency 是啥?
Maven <optional> 标签用于控制依赖的传递性。
<optional>true</optional>means:
“Do NOT pass this dependency to projects that depend on me.”
That’s it.
Nothing more. Nothing less.
It is NOT about runtime optionality.
The mental model (very important)
Think of Maven dependencies as edges in a graph.
optional=true means:
Consumer ←X— Optional DependencyThe arrow stops at the module boundary.
What optional does NOT do (CRITICAL)
❌ Does NOT remove the dependency
❌ Does NOT make it runtime-optional
❌ Does NOT disable compilation
❌ Does NOT affect your own project
If your project declares an optional dependency:
- You still compile with it
- You still run with it
- You still package with it
Only downstream projects are affected.
Example 1: WITHOUT optional (default behavior)
Library A (a framework)
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency>Application B (depends on A)
Dependency graph:
App B
└─ Library A
└─ mysql-connector-java➡ MySQL driver is forced onto everyone, even if they use PostgreSQL.
❌ Bad library design.
Example 2: WITH optional (correct design)
Library A
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
<optional>true</optional>
</dependency>Dependency graph now
App B
└─ Library A➡ MySQL is NOT inherited.
If App B wants MySQL:
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>This gives choice to the consumer.
Example 3: Optional + runtime failure (important!)
Library A
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<optional>true</optional>
</dependency>App B
DriverManager.getConnection("jdbc:postgresql://...");But App B did not declare PostgreSQL explicitly.
➡ Result:
ClassNotFoundException: org.postgresql.DriverThis is expected.
optional means “I might use this, you decide”.
When should you use <optional>?
✔ Correct use cases
- SPI / plugin systems
- Multiple backend choices
- Framework integrations
- Adapters
- Logging bridges
Examples:
- Database drivers
- Messaging clients
- Cloud provider SDKs
- Logging implementations
When you should NOT use it
❌ Applications
❌ Internal modules
❌ Libraries with hard requirements
If your code cannot run without it, it should NOT be optional.
Optional vs Provided (common confusion)
| Feature | optional | provided |
|---|---|---|
| Used at compile | ✔ | ✔ |
| Included transitively | ❌ | ❌ |
| Included in runtime | ✔ | ❌ |
| Provided by container | ❌ | ✔ |
Optional = consumer choice
Provided = runtime environment supplies it
Optional vs Exclusion (very different)
| Concept | optional | exclusion |
|---|---|---|
| Direction | Outbound | Inbound |
| Scope | Library → consumer | Consumer → library |
| Who controls it | Producer | Consumer |
Real-world example: SLF4J
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<optional>true</optional>
</dependency>Why?
- API is required
- Implementation is optional
- Consumer chooses logback / log4j / etc.
This is excellent library design.
How to SEE optional behavior
Run in consumer project:
mvn dependency:treeYou will see:
(logback-classic omitted - optional)This is Maven explicitly telling you what happened.
Key rule to remember (burn this in)
optionalonly affects transitive dependency propagation.
If you remember only one thing, remember that.
Summary (plain English)
optional≠ optional at runtimeoptional= “don’t force this on my users”- Libraries use it, applications almost never
- Consumers must opt-in explicitly