maven runtime and provided scopes are sometimes sources of confusion. When do we need to use them?
compile: This is what you want to use most of the time and is also the default scope. If you use this scope, maven will also handle transitive dependencies and dependency resolution for you automatically
provided: If you specify a dependency as provided, it is used only for compiling. At runtime, your container (usually an application server such as tomcat) is expected to supply the required jar. The common usecase for this scope is those situations when you need certain classes for your code to compile, but at runtime you want to delegate the dependency to a container or JDK. This scope is not transitive
runtime: In this case, the dependency is NOT available in the compile path. At runtime, you have to make the dependency available in the runtime classpath. The common usecase is when you want to make sure that your code does not make any usage of the classes in the dependency which should be dynamically available only at runtime. Usually this is the case with specific JDBC implementations which are loaded using Class.forname. Unlike provided, dependencies are included transitively!
Both runtime and provided hint about requiring something dynamically, but in fact they are to be used for entirely different purposes. If you make a dependency compile where only a runtime would have sufficed, nothing per se will break neither at compile time nor at runtime. But on the other hand, if you make a dependency provided, you have to make sure that at runtime the required dependencies are available. Otherwise you will most likely get a java.lang.ClassNotFoundException