記一次SpringContextHolder.getBean出現異常NoClassDefFoundError: Could not initialize class


代碼如下:

public class TestUtils {
    private static UserDao logDao = SpringContextHolder.getBean(UserDao.class);
    
    public static String getLog(String type){
        return "!23";
    }
}

在controller中使用這Utils的時候出現如下錯誤:

 

奇怪的是在容器中又能得到UserDao,為何初始化這個TestUtils的時候就失敗了呢。

經過一番排查,發現項目啟動的時候有這么一段日志

2019-10-29 15:36:22.839  WARN 21948 --- [           main] o.mybatis.spring.SqlSessionFactoryBean   : Cannot load the 'file [D:\*\utils\TestUtils.class]'. Cause by java.lang.NoClassDefFoundError: Could not initialize class com.*.utils.TestUtils

所以覺得是mybatis的問題,mybaits在掃描所有entity的時候邏輯(mybatis-spring-boot-starter是2.1.1對應的mybatis-spring的版本2.0.3)

    if (hasLength(this.typeAliasesPackage)) {
      scanClasses(this.typeAliasesPackage, this.typeAliasesSuperType).stream()
          .filter(clazz -> !clazz.isAnonymousClass()).filter(clazz -> !clazz.isInterface())
          .filter(clazz -> !clazz.isMemberClass()).forEach(targetConfiguration.getTypeAliasRegistry()::registerAlias);
    }


  private Set<Class<?>> scanClasses(String packagePatterns, Class<?> assignableType) throws IOException {
    Set<Class<?>> classes = new HashSet<>();
    String[] packagePatternArray = tokenizeToStringArray(packagePatterns,
        ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
    for (String packagePattern : packagePatternArray) {
//會先掃描出所有typeAliasesPackage下面所有的類 Resource[] resources
= RESOURCE_PATTERN_RESOLVER.getResources(ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + ClassUtils.convertClassNameToResourcePath(packagePattern) + "/**/*.class"); for (Resource resource : resources) { try { ClassMetadata classMetadata = METADATA_READER_FACTORY.getMetadataReader(resource).getClassMetadata();
//此時會先加載這個類,加載類的時候里面的靜態屬性會初始化,但是此時容器中還有UserDao的Bean實例,所以導致初始化失敗,觸發異常 Class
<?> clazz = Resources.classForName(classMetadata.getClassName());
//此時才判斷superType
if (assignableType == null || assignableType.isAssignableFrom(clazz)) { classes.add(clazz); } } catch (Throwable e) { LOGGER.warn(() -> "Cannot load the '" + resource + "'. Cause by " + e.toString()); } } } return classes; }

上面就是導致出現問題的原因。

看了一下之前的項目,邏輯一樣,但為啥沒出現問題呢,發現之前項目引用的mybatis-spring-boot-starter是1.3.2版本,對應的mybatis-spring的版本的1.3.2。

那么里面的實現是怎么樣呢?為何沒出現類似的錯誤呢?

if (hasLength(this.typeAliasesPackage)) {
    String[] typeAliasPackageArray = tokenizeToStringArray(this.typeAliasesPackage,ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
    for (String packageToScan : typeAliasPackageArray) {
        configuration.getTypeAliasRegistry().registerAliases(packageToScan,
                typeAliasesSuperType == null ? Object.class : typeAliasesSuperType);
        if (LOGGER.isDebugEnabled()) {
          LOGGER.debug("Scanned package: '" + packageToScan + "' for aliases");
        }
    }
}

public void registerAliases(String packageName, Class<?> superType){
    ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>();
    //會先過濾superType
    resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
    Set<Class<? extends Class<?>>> typeSet = resolverUtil.getClasses();
    for(Class<?> type : typeSet){
      // Ignore inner classes and interfaces (including package-info.java)
      // Skip also inner classes. See issue #6
      if (!type.isAnonymousClass() && !type.isInterface() && !type.isMemberClass()) {
        registerAlias(type);
      }
    }
}

 

兩個解決辦法:

1.降低mybatis-spring-boot-starter 的版本到(經測試mybatis-spring.jar的2.0.0及以下版本都可以,其他版本沒看源碼)

2.精確指定到entity的目錄(建議此,減少掃描和遍歷的次數)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM