Maven Dependency Scopes
last modified June 10, 2025
In this article we show how to use different dependency scopes in Maven projects to control when and where dependencies are available during the build lifecycle.
Maven dependency scopes control the availability of dependencies during different phases of the build process. They determine when a dependency is included in the classpath for compilation, testing, and runtime execution.
Overview of Dependency Scopes
Maven provides six different dependency scopes that define when a dependency should be available:
- compile - Available in all phases (default scope)
- runtime - Available at runtime and test phases only
- test - Available only during test compilation and execution
- provided - Available at compile time but not packaged
- system - Similar to provided but must specify the JAR location
- import - Used only for dependency management in POMs
Compile Scope
The compile
scope is the default scope when no scope is
specified. Dependencies with compile scope are available in all
classpaths and are included in the final artifact.
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.example</groupId> <artifactId>dependency-scopes-example</artifactId> <version>1.0.0</version> <packaging>jar</packaging> <properties> <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <!-- Compile scope (default) --> <dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> <version>2.13.1</version> <scope>compile</scope> </dependency> <!-- Same as above, scope is optional for compile --> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.12.0</version> </dependency> </dependencies> </project>
Compile scope dependencies are used throughout the application and are packaged with the final JAR or WAR file.
package com.example; import com.google.gson.Gson; import org.apache.commons.lang3.StringUtils; public class CompileExample { public static void main(String[] args) { // Using compile scope dependencies Gson gson = new Gson(); String json = gson.toJson("Hello World"); String padded = StringUtils.leftPad("Maven", 10, '*'); System.out.println("JSON: " + json); System.out.println("Padded: " + padded); } }
The compile
scope is the default scope for dependencies in
Maven. If no scope is specified, Maven assumes compile
scope.
<scope>compile</scope>
Specifies that the dependency is available during compilation, testing, and runtime phases. This is the default scope.
Runtime Scope
Dependencies with runtime
scope are not required for
compilation but are needed at runtime and for testing.
<dependencies> <!-- Database driver - runtime scope --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.33</version> <scope>runtime</scope> </dependency> <!-- Logging implementation - runtime scope --> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.4.11</version> <scope>runtime</scope> </dependency> <!-- Logging API - compile scope --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>2.0.9</version> </dependency> </dependencies>
package com.example; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class RuntimeExample { private static final Logger logger = LoggerFactory.getLogger(RuntimeExample.class); public static void main(String[] args) { // SLF4J API is compile scope - available at compile time logger.info("Starting application"); // Logback implementation is runtime scope - loaded at runtime logger.debug("Debug message"); logger.warn("Warning message"); System.out.println("Check the logs!"); } }
<scope>runtime</scope>
Indicates that the dependency is not needed for compilation but is required at runtime. Common for database drivers and logging implementations.
Test Scope
Dependencies with test
scope are only available during test
compilation and execution phases. They are not included in the final
artifact.
<dependencies> <!-- JUnit for testing --> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter</artifactId> <version>5.10.0</version> <scope>test</scope> </dependency> <!-- Mockito for mocking --> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> <version>5.5.0</version> <scope>test</scope> </dependency> <!-- AssertJ for fluent assertions --> <dependency> <groupId>org.assertj</groupId> <artifactId>assertj-core</artifactId> <version>3.24.2</version> <scope>test</scope> </dependency> </dependencies>
package com.example; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.DisplayName; import org.mockito.Mockito; import static org.assertj.core.api.Assertions.assertThat; public class CompileExampleTest { @Test @DisplayName("Should create JSON string") public void shouldCreateJsonString() { // Test using test scope dependencies CompileExample example = new CompileExample(); // Using AssertJ for assertions assertThat(example).isNotNull(); // Example of mocking with Mockito String mockString = Mockito.mock(String.class); Mockito.when(mockString.length()).thenReturn(10); assertThat(mockString.length()).isEqualTo(10); } @Test @DisplayName("Should handle string operations") public void shouldHandleStringOperations() { String result = "test"; assertThat(result) .isNotNull() .hasSize(4) .startsWith("te") .endsWith("st"); } }
<scope>test</scope>
Specifies that the dependency is only needed for testing. These dependencies are not included in the final application package.
Provided Scope
Dependencies with provided
scope are available at compile
time but are expected to be provided by the runtime environment.
<dependencies> <!-- Servlet API - provided by web container --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> <scope>provided</scope> </dependency> <!-- JSP API - provided by web container --> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>javax.servlet.jsp-api</artifactId> <version>2.3.3</version> <scope>provided</scope> </dependency> <!-- Lombok - compile time only --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.30</version> <scope>provided</scope> </dependency> </dependencies>
package com.example; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; @WebServlet("/hello") public class ServletExample extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html"); try (PrintWriter out = response.getWriter()) { out.println("<html>"); out.println("<head><title>Provided Scope Example</title></head>"); out.println("<body>"); out.println("<h1>Hello from Servlet!</h1>"); out.println("<p>Servlet API is provided by container</p>"); out.println("</body>"); out.println("</html>"); } } }
package com.example; import lombok.Data; import lombok.Builder; import lombok.AllArgsConstructor; import lombok.NoArgsConstructor; @Data @Builder @NoArgsConstructor @AllArgsConstructor public class LombokExample { private String name; private int age; private String email; public static void main(String[] args) { // Lombok generates getters, setters, equals, hashCode, toString LombokExample person = LombokExample.builder() .name("John Doe") .age(30) .email("john@example.com") .build(); System.out.println("Person: " + person); System.out.println("Name: " + person.getName()); System.out.println("Age: " + person.getAge()); } }
<scope>provided</scope>
Indicates that the dependency is needed for compilation but will be provided by the runtime environment (like a web container or JVM).
System Scope
The system
scope is similar to provided but requires
explicitly specifying the path to the JAR file on the local file system.
<dependencies> <!-- System scope dependency - not recommended --> <dependency> <groupId>com.oracle</groupId> <artifactId>ojdbc8</artifactId> <version>21.1.0.0</version> <scope>system</scope> <systemPath>${project.basedir}/lib/ojdbc8.jar</systemPath> </dependency> </dependencies>
System scope is generally discouraged because it makes the build non-portable. Instead, install the JAR to a local repository or use a corporate repository.
<scope>system</scope> <systemPath>${project.basedir}/lib/ojdbc8.jar</systemPath>
System scope requires specifying the exact path to the JAR file. This makes the build environment-dependent and less portable.
Import Scope
The import
scope is only used in the
dependencyManagement
section to import dependency management
from other POMs.
<dependencyManagement> <dependencies> <!-- Import Spring Boot dependency management --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>3.1.5</version> <type>pom</type> <scope>import</scope> </dependency> <!-- Import JUnit BOM --> <dependency> <groupId>org.junit</groupId> <artifactId>junit-bom</artifactId> <version>5.10.0</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <!-- Versions managed by imported BOM --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter</artifactId> <scope>test</scope> </dependency> </dependencies>
<scope>import</scope> <type>pom</type>
Import scope allows importing dependency management from other POM files, commonly used with Bill of Materials (BOM) POMs.
Scope Transitivity
When a dependency has its own dependencies (transitive dependencies), Maven determines their scope based on both the direct dependency scope and the transitive dependency scope.
<dependencies> <!-- Spring Boot starter has many transitive dependencies --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>3.1.5</version> <scope>compile</scope> </dependency> <!-- Excluding specific transitive dependencies --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> <version>3.1.5</version> <scope>provided</scope> <exclusions> <exclusion> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-websocket</artifactId> </exclusion> </exclusions> </dependency> </dependencies>
Understanding scope transitivity helps you control which transitive dependencies are included in your project and when they are available.
Viewing Dependency Scopes
You can use Maven commands to analyze and understand the dependency scopes in your project.
# View dependency tree with scopes $ mvn dependency:tree # View dependencies by scope $ mvn dependency:list # Analyze dependency scope conflicts $ mvn dependency:analyze # Generate dependency report $ mvn dependency:resolve-sources
These commands help you understand how Maven resolves dependencies and their scopes in your project.
Best Practices for Dependency Scopes
Here are some best practices for using dependency scopes effectively:
- Use test scope for testing frameworks - Keep JUnit, Mockito, and other testing tools in test scope
- Use provided scope for container-provided APIs - Servlet API, EJB API should be provided scope
- Use runtime scope for implementations - Database drivers and logging implementations should be runtime scope
- Avoid system scope - Use local repository installation instead
- Use import scope for BOMs - Import dependency management from Bill of Materials POMs
- Keep compile scope minimal - Only include dependencies that are directly used in source code
Source
Maven Dependency Mechanism - reference
In this article we have shown how to use different dependency scopes in Maven projects to control when and where dependencies are available.
Author
List all Java tutorials.