SLF4J api 和 binding jar 版本不兼容导致的 IllegalAccessError

这是前几天碰到的一个由 SLF4J 引发的异常

Exception in thread "main" java.lang.IllegalAccessError: tried to access field
org.slf4j.impl.StaticLoggerBinder.SINGLETON from class org.slf4j.LoggerFactory
   at org.slf4j.LoggerFactory.<clinit>(LoggerFactory.java:60)

在网上搜索时,找到了 SLF4J FAQ 这篇文档,其中就有对这个异常的描述,当时是把问题解决了。为了将这个问题的解决过程描述方便,我先写了之前的一篇文章,简单介绍了一些 SLF4J 的知识。因为这个异常的产生原因是由于 slf4j-api.jar 与 slf4j-log4j12.jar 版本不兼容所致,其中 slf4j-log4j12.jar 是 SLF4J 提供的五种 binding jar 之一。具体的说,是由于使用了较老版本的 slf4j-api.jar(在我的应用里引的版本是 1.4.1),和较新版本的 slf4j binding jar(在我的应用里引了 slf4j-log4j12-1.5.6.jar)。

错误的出处是: 我定义了一个类,为了使用 log4j 打印日志,调用 org.slf4j.LoggerFactory 创建了一个 Logger,并作为类的 static 属性,除此之外无其他的成员属性,代码如下:

public class Foo {
    private static final Logger logger = LoggerFactory.getLogger(Foo.class);
    // ... methods
}

 在获取 Logger 时抛了前面的异常。

下面通过源码分析下异常的来源:

在 slf4j-api 的早期版本(1.5.5 及之前)中,org.slf4j.LoggerFactory 中包含一个 ILoggerFactory 的 static 属性,并且通过 static 块进行初始化。

public final class LoggerFactory {

  static ILoggerFactory loggerFactory;

  static {
      try { 
        loggerFactory = StaticLoggerBinder.SINGLETON.getLoggerFactory();
      } catch(NoClassDefFoundError ncde) {
      //...
   }
}

 在早期版本的 slf4j binding 实现中,org.slf4j.impl.StaticLoggerBinder 类中的 SINGLETON 成员是被定义为 public 的。

public class StaticLoggerBinder { 

  /**
   * The unique instance of this class.
   */
  public static final StaticLoggerBinder SINGLETON = new StaticLoggerBinder();

  public ILoggerFactory getLoggerFactory() {
    //...
   }
}

 所以,如果 slf4j-api.jar 和 slf4j-log4j12.jar 都使用早期版本,将不会报错。

从 SLF4J 1.5.6 开始, slf4j binding 实现中的 org.slf4j.impl.StaticLoggerBinder 类的 SINGLETON 被定义为了 private. 并且从这个版本开始,slf4j api 中 org.slf4j.LoggerFactory 不再使用 StaticLoggerBinder.SINGLETON.getLoggerFactory() 的方式获取 ILoggerFactory 的实例,而是通过 StaticLoggerBinder.getSingleton.getLoggerFactory() 的方式对 ILoggerFactory 实例进行初始化。具体的实现要比早期版本复杂的多,在此不详细列代码了。

所以,如果使用早期版本的 slf4j-api.jar 和新版本的 slf4j binding jar,由于 org.slf4j.LoggerFactory 无法访问新版本 binding 实现中的 StaticLoggerBinder.SINGLETON 私有属性,就会产生前面的异常信息。

猜你喜欢

转载自wxl24life.iteye.com/blog/1920007