| 
 代码如下:  
 
 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的目录(建议此,减少扫描和遍历的次数)  
   |