Spring hides real errors, making troubleshooting difficult

Posted by rockroka on Sun, 02 Jan 2022 12:48:55 +0100

Hello, I'm looking at the mountain.

Today, after the project relies on a basic component, it failed to start. The troubleshooting process took some detours. Finally, it was confirmed that the Java. Com was caused by the version conflict of dependent components Lang.noclassdeffounderror exception. The following is the troubleshooting process. I hope it can provide you with some ideas.

Observe exception stack

The following is the printed exception stack information, from which you can extract possible key information. You can find "Could not convert argument value of type [java.lang.String] to required type [java.lang.Class]" and "unresolved class definition for class [cn. Howardliu. Demo. Addressmapper]". Continue to look for the occurrence time from the exception stack. It can be found that it is to call abstractautowirecapablebeanfactory When creating a Bean instance, this method is to create a Bean instance.

This is an exception message( getMessage Content of, horizontal too long, manual line wrapping):

org.springframework.beans.factory.UnsatisfiedDependencyException:
    Error creating bean with name 'methodValidationPostProcessor' defined in class path resource [org/springframework/boot/autoconfigure/validation/ValidationAutoConfiguration.class]:
    Unsatisfied dependency expressed through method 'methodValidationPostProcessor' parameter 0;
    nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException:
    Error creating bean with name 'addressMapper' defined in file [/Users/liuxinghao/Documents/work/code/cn.howardliu/effective-spring/target/classes/cn/howardliu/demo/AddressMapper.class]:
    Unsatisfied dependency expressed through constructor parameter 0:
    Could not convert argument value of type [java.lang.String] to required type [java.lang.Class]:
    Failed to convert value of type 'java.lang.String' to required type 'java.lang.Class';
    nested exception is java.lang.IllegalArgumentException: 
    Unresolvable class definition for class [cn.howardliu.demo.AddressMapper]

The following is the exception stack:

	at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:799) ~[spring-beans-5.2.13.RELEASE.jar:5.2.13.RELEASE]
	at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:540) ~[spring-beans-5.2.13.RELEASE.jar:5.2.13.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1341) ~[spring-beans-5.2.13.RELEASE.jar:5.2.13.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1181) ~[spring-beans-5.2.13.RELEASE.jar:5.2.13.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:556) ~[spring-beans-5.2.13.RELEASE.jar:5.2.13.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:516) ~[spring-beans-5.2.13.RELEASE.jar:5.2.13.RELEASE]
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:324) ~[spring-beans-5.2.13.RELEASE.jar:5.2.13.RELEASE]
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.2.13.RELEASE.jar:5.2.13.RELEASE]
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:322) ~[spring-beans-5.2.13.RELEASE.jar:5.2.13.RELEASE]
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:207) ~[spring-beans-5.2.13.RELEASE.jar:5.2.13.RELEASE]

Other exception stack information can be ignored

We can check according to the currently valid information. First, take a look at our CN howardliu. demo. Whether there is a problem with the addressmapper definition, and then see if there is a problem with the Service that depends on it. No problem has been found. The next check point is the configuration, such as whether the @ MapperScan is correct and whether the @ Mapper annotation is added to the Mapper class.

We can't find the idea from the exception information, so we can only start with the code.

It should be said here that printing exception information is very important, which directly affects our thinking of troubleshooting. If the printed information cannot be accurately located, we will spend a lot of time looking for the real error, which requires walking through the code and sometimes some experience.

Positioning problem

We use the exception stack ` ` constructorresolver Start with createargumentarray (constructorresolver. Java: 799), follow the breakpoint and eventually catch up with org. Org springframework. util. Classutils#forname method. The code that will throw an exception is as follows:

try {
    return Class.forName(name, false, clToUse);
}
catch (ClassNotFoundException ex) {
    int lastDotIndex = name.lastIndexOf(PACKAGE_SEPARATOR);
    if (lastDotIndex != -1) {
        String innerClassName =
                name.substring(0, lastDotIndex) + INNER_CLASS_SEPARATOR + name.substring(lastDotIndex + 1);
        try {
            return Class.forName(innerClassName, false, clToUse);
        }
        catch (ClassNotFoundException ex2) {
            // Swallow - let original exception get through
        }
    }
    throw ex;
}

The error is class For name (name, false, cltouse), name passes the "cn.howardliu.demo.AddressMapper" string, and the exception thrown is Java Lang. NoClassDefFoundError, because it is not a ClassNotFoundException exception, will not enter the catch logic and will be thrown upward directly.

If we find the error, we can locate the problem.

Generally speaking, Java Lang.noclassdeffounderror error error is that the class to be loaded can be found, but an exception occurs during loading. In short, there is a problem with the definition of the class. We decompile and run the jar package with the help of JD-GUI. The results are as follows:

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import cn.howardliu.demo.Address;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface AddressMapper extends BaseMapper<Address> {}

If you observe carefully, you can see import com baomidou. mybatisplus. core. mapper. BaseMapper; There is no underline in this line, that is, the interface cannot be traced in the decompilation tool. It is inferred that the class definition basemapper cannot be found in the running environment.

So, when class When forname loads the class, it throws Java Lang.noclassdeffounderror exception.

solve the problem

If you have some experience, you will immediately think that there are probably jar dependent version conflicts.

We can find the dependency of version conflict through maven command line:

mvn dependency:tree -Dverbose | grep conflict

The print result is:

[INFO] |  +- (com.baomidou:mybatis-plus:jar:3.1.2:compile - omitted for conflict with 2.1.6)

We can also use the visualization tool of IDEA in POM Open dependency graph on XML:

We can see that the red line of mybatis plus indicates the conflict information:

The conclusion is that the version of mybatis plus conflicts, and the project relies on mybatis plus 2.1 6 and 3.1 2 two versions due to 2.1 6. The path is shorter and finally selected.

At this time, you only need to remove the dependency of the lower version.

Double disk problem

Version problem of mybatis plus

Why does the lower version of mybatis plus cause class loading failure? This is because when mybatis plus is updated across versions, the package path of BaseMapper is changed:

// 3.1. Version 2
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
// 2.1. Version 6
import com.baomidou.mybatisplus.mapper.BaseMapper;

Moreover, there are more than one, such as IService, ServiceImpl, TableName, TableField, Model, TableField, etc. many commonly used classes have changed their positions. Therefore, the dependent class cannot be found. The compilation is 3.1 2. If the dependency is still in the running environment, there will be no problem with compilation, and there will be loading class exceptions during execution.

To solve this problem in an engineering way, we can create the basic dependency bom configuration and define the basic dependency package, which is not in the specified version in the project. In this way, the unified version can effectively avoid such problems.

We can also add conflict dependency check in CI/CD. If a conflict dependency is found, the pipeline will be terminated.

Real exception hidden problem

Next, let's see why it's Java Lang.noclassdeffounderror exception. As a result, a pile of irrelevant errors are printed in the exception stack. Continue to follow the breakpoint Debug:

org.springframework.util.ClassUtils#resolveClassName will catch the LinkageError error error and then package it as an IllegalArgumentException exception. At this time, it is really an exception or continue to throw it up.

Then at org springframework. beans. The typeconvertersupport#convertifnecessary method will be wrapped as a typemismatch exception. At this time, the real exception is still in the exception cause parameter and is not lost.

When we get back to org springframework. beans. factory. support. After the constructorresolver #createargumentarray method, the methods to catch exceptions are:

try {
    convertedValue = converter.convertIfNecessary(originalValue, paramType, methodParam);
}
catch (TypeMismatchException ex) {
    throw new UnsatisfiedDependencyException(
            mbd.getResourceDescription(), beanName, new InjectionPoint(methodParam),
            "Could not convert argument value of type [" +
                    ObjectUtils.nullSafeClassName(valueHolder.getValue()) +
                    "] to required type [" + paramType.getName() + "]: " + ex.getMessage());
}

At this time, we can notice that when packaging the unsatisfied dependencyexception, we just append the captured typemismatch exception to the exception description through the getMessage method. At this time, it is repackaged after the previous rounds of packaging, The real exception information is only the processed information of unresolved class definition for class [cn. Howardliu. Demo. Addressmapper], and there is no Java Lang. NoClassDefFoundError.

So far, the real anomaly disappeared.

This also gives us a reminder. We must ensure that when exceptions occur, we must keep valid information, otherwise, troubleshooting will be very troublesome.

Summary at the end of the paper

This paper is an essay on catching insects, starting from the problem to solving the problem, and gives a complete idea. java.lang.NoClassDefFoundError usually occurs in the event of version conflict. This exception is that there is no problem with compilation and packaging, and class loading fails at runtime. The reason why we took some detours in troubleshooting in this article is that Spring hides real exceptions, which hinders our troubleshooting. Therefore, we should also pay attention to the clear information of exceptions in daily development, which can provide us with accurate goals for troubleshooting.

Green mountains don't change and green water flows. I'll see you next time.

Recommended reading

Hello, I'm looking at the mountain. Swim in the code world and enjoy life. If the article is helpful to you, please like, collect and pay attention to it. I also compiled some excellent learning materials, and I would like to pay attention to the official account of "the mountain view cabin" and get the reply to "information".

Personal homepage: https://www.howardliu.cn
Personal blog: Mybatis plus version conflict triggers "Could not convert argument value of type [java.lang.String] to required type [java.lang.Class]" Lang.noclassdeffounderror exception
CSDN home page: https://kanshan.blog.csdn.net/
CSDN blog: Mybatis plus version conflict triggers "Could not convert argument value of type [java.lang.String] to required type [java.lang.Class]" Lang.noclassdeffounderror exception

👇🏻 Welcome to my official account "look at the hill house" and collect the selected information. 👇🏻

Topics: Java Mybatis Spring Spring Boot