Reputation: 13
I'm curious to know how Intellij can resolve the dependency conflicts? Let me explain my situation. I should work on the spring boot application. It uses Maven. IntelliJ can build and run the application without any problem, but when I make a jar file,
mvn clean package
and run the jar file
java -jar xxx.jar
I faced a java.lang.NoSuchMethodError
. Some conflicts on dependencies caused it, and my application uses the wrong version of a jar file.
I want to know how IntelliJ can find the correct jar file which contains the method, while it uses the same pom.xml, which I face error while using with the mvn command.
And is it possible to find that which version of every jar file used by IntelliJ? (I want to use this for correcting the pom file)
Thanks
Upvotes: 0
Views: 456
Reputation: 103637
There is a big difference between runtime and compile time when it comes down to such things.
compiling code (such as mvn package
), and writing code (as in, what IntelliJ is doing to ensure that your editing experience is nice; auto-complete dialogs for all the libraries you use, errors if you try to invoke non-existent methods, etcetera) are one thing (let's call it 'write time').
Running your code, as with java -jar xxx.jar
is something completely different.
maven and your dependency list is a write time thing. Thus, maven and intellij know where to look for your dependencies, but when you run java -jar xxx.jar
, that does not know where to look, and thus, your dependencies aren't found, and thus, NoClassDefFoundError
occurs.
That's because that jar file that maven makes just contains your code, it is completely disconnected from your maven file (your maven stuff is not looked up when you run your code at all), and it does not contain your dependencies.
You'd have to ship them separately. There are 3 solutions to this problem:
java -jar
to run jar files; you can't specify a -classpath
parameter). This lets you deploy your app such that your app installation looks like:/usr/local/projects/YourApp/main-app.jar
/usr/local/projects/YourApp/dep/guava.jar
/usr/local/projects/YourApp/dep/mysql-jdbc-connector.jar
/usr/local/projects/YourApp/dep/jdbi.jar
etcetera, and to run this application, just run main-app.jar
. This requires the manifest of main-app.jar to contain the entry:
Class-Path: dep/guava.jar dep/mysql-jdbc-connector.jar dep/jdbi.jar
When running the jar, the Class-Path
entry is split on spaces, and then each entry is looked up relative to the directory that contains main-app.jar
. By shipping the jars separately, it's easy to separately update them or replace them, and deploying a new version is much faster (you just ship the jar(s) that were changed, not all of them. Many apps have hundreds of MBs of deps, whereas their own jar is a few MB at most, makes a big difference for example when pushing deps from your dev machine to the test server!)
This leads to the question: How do you make maven put that Class-Path entry in the output jar file's manifest? The maven-jar-plugin
can do the job - see this answer for more details.
This is the notion of taking all your deps, rewriting their name to avoid version conflicts, and then making one humongous jar file that contains everything. It has the considerable downside of being a much slower process, especially if you need to push this out to another system for testing. Use the shade plugin to do so.
java -jar
.java -jar x.jar
cannot work unless the jar file either has a Class-Path entry in its manifest, or contains every dep it needs. However, you can also run your java code like so: java -cp main-app.jar:dep/guava.jar:dep/jdbi.jar:/dep/other-deps-here com.foo.YourMainApp
. This is.. not convenient, but you could presumably write a shell script or some such. This isn't a very java-like solution, I don't recommend it.
Upvotes: 0