[JAVA Basics] NoSuchMethodError when different jars have the same fully qualified class name and different method Method, class loading problem with the same name / parental delegation

foreword

I encountered a project engineering problem in my work recently. There are two different versions of the jwt jar package in the classpath of the startup jvm. It is reported that there are two java.lang.NoSuchMethodError:different versions of the jar package in the classpath at the calling place, and there are this class in it, the higher version. There is no such Method in the jar , but the lower version has this Method. In the end, the method that did not load this higher version
guessed that the problem was the "fully qualified class name". It was exactly the same, Cloassloader only loaded the higher version of the jar package.

This article is to verify the process of how jvm loads the same class with the same fully qualified class name in the classpath.

Preparing for the project

Prepare three different jars, all of which have the same class Car, as follows:

  • demo-audi-1.0.jar
  • demo-audi-2.0.jar
  • demo-mercedes-1.0.jar

These three projects have the sameclass: com.example.demo.Car

demo-audi-1.0.jar environment

public class Car {
    
    

    private static final String version = "A4L";

    public String getVersion() {
    
    
        return version;
    }

    public String getName() {
    
    
        return "audi";
    }

    public Integer limitSpeed() {
    
    
        return 100;
    }

    public String seatPerson(Integer a) {
    
    
        return "可以坐 :" + a;
    }
}

demo-audi-2.0.jar environment

public class Car {
    
    

    private static final String version = "A6L";

    public String getVersion() {
    
    
        return version;
    }

    public String getName() {
    
    
        return "audi";
    }

    public Integer limitSpeed() {
    
    
        return 140;
    }

    public String seatPerson() {
    
    
        return "可以坐 :5";
    }
}

demo-mercedes-1.0.jar contents

public class Car {
    
    

    private static final String version = "E300L";

    public String getVersion() {
    
    
        return version;
    }

    public String getName() {
    
    
        return "mercedes";
    }

    public Integer limitSpeed() {
    
    
        return 135;
    }

    public String hasPerson() {
    
    
        return "能舒服的坐 :4";
    }
}

insert image description here

Start demo function

Prepare the Example class

example.java

public class Example {
    
    

    public static void main(String[] args) {
    
    
        Car car = new Car();
        System.out.println("当前车辆版本:" + car.getVersion());
        System.out.println("当前 jar 包路径 : ");
        System.out.println(car.getClass().getProtectionDomain().getCodeSource().getLocation().getPath());
        Method[] declaredMethods = car.getClass().getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
    
    
            System.out.println("------------------");
            System.out.println("method name: " + declaredMethod.getName());
            List<String> collect = Arrays.stream(declaredMethod.getParameterTypes()).map(Class::getName).collect(Collectors.toList());
            if(!collect.isEmpty()) {
    
    
                System.out.println("parameter type : " + collect);
            }
            System.out.println("------------------");
        }
    }
}

Prepare Classpath

test directory

demo-audi-1.0.jar     demo-audi-2.0.jar     demo-example-1.0.jar  demo-mercedes-1.0.jar

demo function

Natural order, * to replace all jars

java -classpath "/test/*"  com.example.demo.Example

当前车辆版本:A4L
当前 jar 包路径 :
/test/demo-audi-1.0.jar
------------------
method name: getName
------------------
------------------
method name: getVersion
------------------
------------------
method name: limitSpeed
------------------
------------------
method name: seatPerson
parameter type : [java.lang.Integer]
------------------

com.example.demo.CarWhen running, the JVM loads the class Car. According to the selection of the [operating system], the class file of the class demo-audi-1.0.jar is loaded this time.

Manually change the jar name, change the order

When renaming demo-audi-1.0.jar to zemo-audi-1.0.jar , "d" is changed to "z"

➜  test mv demo-audi-1.0.jar zemo-audi-1.0.jar
➜  test java -classpath "/test/*"  com.example.demo.Example
当前车辆版本:A6L
当前 jar 包路径 :
/test/demo-audi-2.0.jar
------------------
method name: getName
------------------
------------------
method name: limitSpeed
------------------
------------------
method name: getVersion
------------------
------------------
method name: seatPerson
------------------

com.example.demo.CarWhen running, the JVM loads the class Car. According to the selection of the [operating system], the class file of the class demo-audi-2.0.jar is loaded this time.

Manually change the jar name and version, change the order

When renaming demo-audi-2.0.jar to demo-mercedes-2.0.jar , "audi" is changed to "mercedes"

[/test]# mv demo-audi-2.0.jar zemo-mercedes-2.0.jar
[/test]# ls demo-example-1.0.jar  demo-mercedes-1.0.jar demo-mercedes-2.0.jar zemo-audi-1.0.jar
[/test]# java -classpath "/test/*"  com.example.demo.Example
当前车辆版本:A6L
当前 jar 包路径 :
/test/demo-mercedes-2.0.jar
------------------
method name: getName
------------------
------------------
method name: getVersion
------------------
------------------
method name: limitSpeed
------------------
------------------
method name: seatPerson
------------------

com.example.demo.CarWhen running, the JVM loads the class Car. According to the selection of the [operating system], the class file of the class demo-mercedes-2.0.jar is loaded this time.

keep only one jar

If the two jars of audi are moved out, only demo-mercedes-1.0.jar is left in the classpath

当前车辆版本:E300L
当前 jar 包路径 :
/test/demo-mercedes-1.0.jar
------------------
method name: getName
------------------
------------------
method name: limitSpeed
------------------
------------------
method name: getVersion
------------------
------------------
method name: hasPerson
------------------

com.example.demo.CarWhen running, the JVM loads the class Car. According to the selection of the [operating system], the class file of the class demo-mercedes-1.0.jar is loaded this time.

Manually specify the package order of Classpath (emphasis)


# 当 demo-audi-1.0.jar在第一个时
java -classpath /test/demo-audi-1.0.jar:/test/demo-audi-2.0.jar:/test/demo-mercedes-1.0.jar:/test/demo-example-1.0.jar com.example.demo.Example

当前车辆版本:A4L
当前 jar 包路径 :
/test/demo-audi-1.0.jar
------------------
method name: getName
------------------
------------------
method name: limitSpeed
------------------
------------------
method name: getVersion
------------------
------------------
method name: seatPerson
parameter type : [java.lang.Integer]

# 当 demo-audi-2.0.jar在第一个时
java -classpath /test/demo-audi-2.0.jar:/test/demo-audi-1.0.jar:/test/demo-mercedes-1.0.jar:/test/demo-example-1.0.jar com.example.demo.Example

当前车辆版本:A6L
当前 jar 包路径 :
/test/demo-audi-2.0.jar
------------------
method name: getName
------------------
------------------
method name: limitSpeed
------------------
------------------
method name: getVersion
------------------
------------------
method name: seatPerson
------------------

# 当 demo-mercedes-1.0.jar在第一个时
java -classpath /test/demo-mercedes-1.0.jar:/test/demo-audi-2.0.jar:/test/demo-audi-1.0.jar:/test/demo-example-1.0.jar com.example.demo.Example
当前车辆版本:E300L
当前 jar 包路径 :
/test/demo-mercedes-1.0.jar
------------------
method name: getName
------------------
------------------
method name: limitSpeed
------------------
------------------
method name: getVersion
------------------
------------------
method name: hasPerson
------------------

in conclusion

According to the JVM's parent delegation model, by default, classes with the same fully qualified class name will only be loaded once, so when the JVM loads the Car class, it will only load from demo-audi-1.0.jar or demo-audi-2.0.jar and demo-mercedes -1.0.jar choose one;

The two Car classes with the same name come from three different Jar packages. They are at the same level. According to the JVM’s class loading mechanism—the parental delegation model , classes with the same fully qualified class name will only be loaded once by default (unless the parental delegation is manually broken. Model);

The classes in the Jar package are loaded using AppClassLoader, and there is a concept of namespace in the class loader. Under the same class loader, the class with the same package name and class name will only be loaded once. If it has already been loaded, directly used loaded;

If there are multiple classes with the same fully qualified class name in the dependency, which class will the JVM load?
A more reliable statement is that the operating system itself controls the default loading order of Jar packages; that is to say, it is not clear and uncertain to us!

The loading order of the Jar package is related to the classpath parameter. When using the idea to start the springboot service, you can see the classpath parameter; the earlier the package path is, the earlier it is loaded;

In other words, if the class in the previous Jar package is loaded, and there is a class with the same name and the same path in the later Jar package, it will be ignored and will not be loaded;

Guess you like

Origin blog.csdn.net/u013412066/article/details/129965950