What 12factor is pushing you towards is more of a microservice approach, where every app is an independent network service. Your app should not depend on a particular server to run, it should be a server.
Of course your application might also need to communicate with other network services, for example a database. The 12factor solution is to treat such external services as configuration. A typical approach is to provide the application with an environment variable that contains an URL under which the network service can be reached.
It turns out that Docker containers (and similar technologies, in particular Kubernetes) are a pretty good model for 12factor apps. The container encapsulates any potential dependencies (can’t access stuff on the host system), and the only thing exposed by a typical container is an open network port. The host system can then forward the port to make the service available to the world.
There is an argument that in the Java world, instead of a container, you’d rather use something like a WAR or JAR (especially since Java 9 makes dependency isolation possible), and that instead of a host operating system you’d have a Java container like Tomcat/Catalina. While this makes it possible to get some of the 12factor advantages, you’re still limited to staying within the JVM. Going native is far more flexible, even if that native solution is actually a JVM-based server running your app. My personal opinion is that any of these JavaEE-isms, servlets, and deployments via WARs are completely anachronistic in today’s environment. If you want to use Java (which is perfectly fine), just embed a Jetty server.