Search code examples
spring-bootmavenintellij-ideacompiler-errorsdependencies

IntelliJ can't resolve classes from my library


I created a library with these pom.xml:

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.3.0</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>spring.boot</groupId>
    <artifactId>jwt</artifactId>
    <version>0.0.1</version>
    <name>jwt</name>
    <description>A library of the boilerplate configuration for securing a Spring Boot API with JWT  </description>
    <properties>
        <java.version>21</java.version>
    </properties>
    <packaging>jar</packaging>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-jackson</artifactId>
            <version>0.11.5</version>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-impl</artifactId>
            <version>0.11.5</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-api</artifactId>
            <version>0.11.5</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

I packaged it via mvn clean install. Then, to test it, I added it to my local maven repository: ~/.m2/repository by running: mvn install:install-file -Dfile=jwt-0.0.1.jar -DgroupId=spring.boot -DartifactId=jwt -Dversion=0.0.1 -Dpackaging=jar and included it in a different project's pom.xml like so:

<dependency>
            <groupId>spring.boot</groupId>
            <artifactId>jwt</artifactId>
            <version>0.0.1</version>
</dependency>

But, IntelliJ still shows can't find symbol when I try to use the classes in the library. enter image description here

My trouble-shooting so far:

  • I have confirmed that the library was indeed saved to my local maven repo
  • I have invalidated caches and restarted my IntelliJ
  • I have also reviewed my jar's directory structure

enter image description here

How can I fix this guys?


Solution

  • TLDR:

    Remove spring-boot-maven-plugin in your library pom and you are done.

    Full answer:

    You used spring-boot-maven-plugin which creates an executable uber-jar in a special format.

    The Nested JARs section explains:

    Java does not provide any standard way to load nested jar files (that is, jar files that are themselves contained within a jar). This can be problematic if you need to distribute a self-contained application that can be run from the command line without unpacking.

    To solve this problem, many developers use “shaded” jars. A shaded jar packages all classes, from all jars, into a single “uber jar”. The problem with shaded jars is that it becomes hard to see which libraries are actually in your application. It can also be problematic if the same filename is used (but with different content) in multiple jars. Spring Boot takes a different approach and lets you actually nest jars directly.

    The Executable Jar File Structure

    Spring Boot Loader-compatible jar files should be structured in the following way:

    example.jar
    |
    +-META-INF
    |  +-MANIFEST.MF
    +-org
    |  +-springframework
    |     +-boot
    |        +-loader
    |           +-<spring boot loader classes>
    +-BOOT-INF
       +-classes
       |  +-mycompany
       |     +-project
       |        +-YourClasses.class
       +-lib
          +-dependency1.jar
          +-dependency2.jar
    

    Application classes should be placed in a nested BOOT-INF/classes directory. Dependencies should be placed in a nested BOOT-INF/lib directory.

    This is precisely the format you see in your IDE.

    Unfortunately, this jar in this format cannot be consumed as a library.

    You need to package your library as a regular jar (ideally, not an uber-jar - maven will resolve the transitive dependencies)