门面模式

为什么要写?

工作中,可能我们本系统中用的是logback框架,然而当我们引入别人的A包时,包中的日志打印框架为log4j,导致包冲突,因此学习一下,记录一下。

什么是log4j、logback、slf4j-simple框架?

它们都是日志框架,拥有具体实现。

  • log4j是apache实现的一个开源日志组件。
  • logback同样是由log4j的作者设计完成的,拥有更好的特性,用来取代log4j的一个日志框架。
  • slf4j提供的一个简单实现。

什么是slf4j?

slf4j是日志门面,只是实现了这些日志框架的一些通用接口,具体实现是那些框架所实现的。

  • logback是直接实现了slf4j接口,因此不消耗内存和计算开销。
  • log4j不是对slf4j的原生实现,所以slf4j在调用log4j时,需要一个适配器。
  • slf4j-api:是日志接口
  • slf4j-simple:是slf4j提供的一个简单实现。

在任何一个项目中,都不允许单独使用日志框架。(阿里巴巴开发手册已强调)
假设项目中已经使用了log4j,而我们此时加载了一个类库,而这个类库依赖另一个日志框架。这个时候我们就需要维护两个日志框架,这是一个非常麻烦的事情。而使用了slf4j就不同了,由于应用调用的抽象层的api,与底层日志框架是无关的,因此可以任意更换日志框架。

所涉及的模式-门面(外观)模式

定义

通过创建一个统一的类,用来包装子系统中一个或多个复杂的类,客户端可以通过调用外观类的方法来调用内部子系统中所有方法

原本客户端是直接通过调用各个子系统的,通过门面模式,创建一个外观类,使得客户端直接调用外观类,来间接去操作相关子系统。

主要作用
  • 实现客户端与子系统之间的松耦合(子系统的修改,不会影响到客户端)
  • 降低原有系统的复杂度(例如:各个电器之间的开关按钮,如果客户端需要关闭整个房间里的电器,那么原来要调用所有电器的off方法,而现在只需要调用外观类的off方法即可)
  • 提高客户端便捷性,使客户端无需关心子系统的工作细节,通过外观类调用。
解决的问题
  • 1.避免了系统与系统之间的高度耦合。
  • 2.使得复杂的子系统使用起来更简单。
优缺点

优点:

  • 实现了子系统与客户端之间的松耦合关系

1.只是提供了一个访问子系统的统一入口,并不影响用户直接使用子系统类;
2.减少了与子系统的关联对象,实现了子系统与客户之间的松耦合关系,松耦合使得子系统的组件变化不会影响到它的客户;

  • 外观模式对客户屏蔽了子系统组件,从而简化了接口,减少了客户处理的对象数目并使子系统的使用更加简单

1.引入外观角色之后,用户只需要与外观角色交互;
2.用户与子系统之间的复杂逻辑关系由外观角色来实现

  • 降低原有系统的复杂度和系统中的编译依赖性,并简化了系统在不同平台之间的移植过程

因为编译一个子系统一般不需要编译所有其他的子系统。一个子系统的修改对其他子系统没有任何影响,而且子系统内部变化也不会影响到外观对象。

缺点:

  • 在不引入抽象外观类的情况下,增加新的子系统可能需要修改外观类或客户端的源代码,违背了“开闭原则”。
  • 不能很好地限制客户使用子系统类,如果对客户访问子系统类做太多的限制则减少了可变性和灵活性

阅读源码

pom添加依赖:

 <!--slf4j只负责提供接口,日志门面-->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.5</version>
        </dependency>
        <!--logback日志框架-->
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.3</version>
        </dependency>
        <!--log4j日志框架-->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
        <!--log4j适配转换-->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.21</version>
        </dependency>
        <!--slf4j简单实现-->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
            <version>1.7.25</version>
        </dependency>

当我们只引入了slf4j-api包,执行日志打印操作控制台会报错。
在这里插入图片描述
明确告知我们没有日志实现类。

引入三个日志框架后,执行结果如下:

SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/Users/yinxin/.m2/repository/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/Users/yinxin/.m2/repository/org/slf4j/slf4j-log4j12/1.7.21/slf4j-log4j12-1.7.21.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/Users/yinxin/.m2/repository/org/slf4j/slf4j-simple/1.7.25/slf4j-simple-1.7.25.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [ch.qos.logback.classic.util.ContextSelectorStaticBinder]
19:57:20.867 [main] ERROR com.lion.LogBean - print log

开始阅读

//得到日志具体类
public static Logger logger = LoggerFactory.getLogger(LogBean.class);

进入到里面

public static Logger getLogger(Class clazz) {
    return getLogger(clazz.getName());
}

将className获取,作为参数传入

public static Logger getLogger(String name) {
    ILoggerFactory iLoggerFactory = getILoggerFactory();
    return iLoggerFactory.getLogger(name);
}

/** ILoggerFactory实例绑定是在类编译时期完成的 */
 public static ILoggerFactory getILoggerFactory() {
    if (INITIALIZATION_STATE == UNINITIALIZED) {
      INITIALIZATION_STATE = ONGOING_INITIALIZATION;
      performInitialization();
    }
    switch (INITIALIZATION_STATE) {
      case SUCCESSFUL_INITIALIZATION:
        return StaticLoggerBinder.getSingleton().getLoggerFactory();
      case NOP_FALLBACK_INITIALIZATION:
        return NOP_FALLBACK_FACTORY;
      case FAILED_INITIALIZATION:
        throw new IllegalStateException(UNSUCCESSFUL_INIT_MSG);
      case ONGOING_INITIALIZATION:
        // support re-entrant behavior.
        // See also http://bugzilla.slf4j.org/show_bug.cgi?id=106
        return TEMP_FACTORY;
    }
    throw new IllegalStateException("Unreachable code");
}

getILoggerFactory()获取到一个工厂类,这个ILoggerFactory是一个接口,可以看到每个日志框架都有一个工厂类实现了这个接口。
在这里插入图片描述

getILoggerFactory()说的是,如果未初始化工厂类,那么进行初始化,如果成功初始化,那么调用StaticLoggerBinder.getSingleton().getLoggerFactory()来进行获取工厂类。

我们看一下performInitialization()方法

private static String STATIC_LOGGER_BINDER_PATH = "org/slf4j/impl/StaticLoggerBinder.class";

private final static void performInitialization() {
    bind();
    if (INITIALIZATION_STATE == SUCCESSFUL_INITIALIZATION) {
      versionSanityCheck();
    }
}

private final static void bind() {
    try {
      Set staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet();
      reportMultipleBindingAmbiguity(staticLoggerBinderPathSet);
      // the next line does the binding
      StaticLoggerBinder.getSingleton();
      INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION;
      reportActualBinding(staticLoggerBinderPathSet);
      emitSubstituteLoggerWarning();
    } catch (NoClassDefFoundError ncde) {
      String msg = ncde.getMessage();
      if (messageContainsOrgSlf4jImplStaticLoggerBinder(msg)) {
        INITIALIZATION_STATE = NOP_FALLBACK_INITIALIZATION;
        Util.report("Failed to load class \"org.slf4j.impl.StaticLoggerBinder\".");
        Util.report("Defaulting to no-operation (NOP) logger implementation");
        Util.report("See " + NO_STATICLOGGERBINDER_URL
                + " for further details.");
      } else {
        failedBinding(ncde);
        throw ncde;
      }
    } catch (java.lang.NoSuchMethodError nsme) {
      String msg = nsme.getMessage();
      if (msg != null && msg.indexOf("org.slf4j.impl.StaticLoggerBinder.getSingleton()") != -1) {
        INITIALIZATION_STATE = FAILED_INITIALIZATION;
        Util.report("slf4j-api 1.6.x (or later) is incompatible with this binding.");
        Util.report("Your binding is version 1.5.5 or earlier.");
        Util.report("Upgrade your binding to version 1.6.x.");
      }
      throw nsme;
    } catch (Exception e) {
      failedBinding(e);
      throw new IllegalStateException("Unexpected initialization failure", e);
    }
}

private static Set findPossibleStaticLoggerBinderPathSet() {
    // use Set instead of list in order to deal with  bug #138
    // LinkedHashSet appropriate here because it preserves insertion order during iteration
    Set staticLoggerBinderPathSet = new LinkedHashSet();
    try {
      ClassLoader loggerFactoryClassLoader = LoggerFactory.class
              .getClassLoader();
      Enumeration paths;
      if (loggerFactoryClassLoader == null) {
        paths = ClassLoader.getSystemResources(STATIC_LOGGER_BINDER_PATH);
      } else {
        paths = loggerFactoryClassLoader
                .getResources(STATIC_LOGGER_BINDER_PATH);
      }
      while (paths.hasMoreElements()) {
        URL path = (URL) paths.nextElement();
        staticLoggerBinderPathSet.add(path);
      }
    } catch (IOException ioe) {
      Util.report("Error getting resources from path", ioe);
    }
    return staticLoggerBinderPathSet;
  }

进入bind方法,findPossibleStaticLoggerBinderPathSet方法是用来获取StaticLoggerBinder.class对象集合的,然后执行reportMultipleBindingAmbiguity方法,这个方法是打印警告信息如果在类路径上发现了多个StaticLoggerBinder。紧接着实例化StaticLoggerBinder对象,指定真正所用到的,这个看类加载器喜欢哪个加载哪个吧。reportActualBinding方法打印告知选择的是哪一个StaticLoggerBinder对象,然后返回。通过该类来得到一致具体的工厂类,通过这个工厂类来得到具体的日志类,从而让我们的客户端可以打印输出。

带你弄清混乱的JAVA日志体系!
http://ju.outofmemory.cn/entry/320663
https://www.cnblogs.com/bigdataZJ/p/springboot-log.html

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值