Library dependencies in a Gradle project are declared in the dependencies block of build.gradle, categorized by configuration (implementation, api, testImplementation, etc.) that controls both their scope and visibility. Gradle resolves them, along with their transitive dependencies, from repositories declared in the repositories block.
Key Points: • Each dependency is listed with its group, artifact, and version, e.g. 'com.google.guava:guava:33.0.0-jre'. • implementation hides a dependency from consumers of your library at compile time, while api exposes it transitively — important for build performance and encapsulation. • testImplementation scopes a dependency to the test source set only, keeping test-only libraries out of the production classpath. • runtimeOnly includes a dependency at runtime but not at compile time, useful for things like JDBC drivers. • Repositories like mavenCentral() or a private Nexus/Artifactory instance must be declared so Gradle knows where to fetch the artifacts from.
Example: A service module might declare implementation 'org.springframework.boot:spring-boot-starter-web' for its runtime web dependency and testImplementation 'org.junit.jupiter:junit-jupiter:5.10.2' scoped only to tests, keeping the production classpath clean of test tooling.
Code Example:
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web:3.2.5'
runtimeOnly 'com.mysql:mysql-connector-j:8.3.0'
testImplementation 'org.junit.jupiter:junit-jupiter:5.10.2'
}Interview Tip: A concise interview answer is:
"I declare dependencies in the dependencies block, choosing the right configuration for scope and visibility — implementation for internal use, api when it needs to be exposed transitively, and testImplementation for test-only libraries. Gradle then resolves everything, including transitive dependencies, from whatever repositories I've declared, like mavenCentral() or an internal Artifactory."