2020-06-08 09:09 
全me村的希望 
阅读(
评论(
编辑 
收藏

  1.为什么需要使用适配器?
      集成第三方日志组件,屏蔽日志组件底层实现,统一提供写日志的接口。

  2.什么是适配器模式
   定义:将一个类的接口变成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够一起工作。

  

  client–>Target(统一接口) Adapter继承Target,并封装Adaptee对象 Adaptee类做具体的工作
  Target目标角色:
  该角色定义把其他类转换为何种接口
  Adaptee源角色:
  你想把谁转换成目标角色,这个“谁”就是源角色,他是已经存在的角色。
  Adapter 适配器角色:
  适配器模式的核心角色,其他两个角色都是已经存在的角色,而适配器角色是需要新建立的,它的职责是把源角色转换为目标角色,实现方式是通过继承或者组合的方式.

  3.mybatis中日志适配

         

   从commons包到stdout都可视为适配器角色,Log接口为目标角色,LogFactory为创建具体日志组件适配器的工厂。

  适配关系如下:

       

  

  从源码角度看:
  目标角色:Log

  1. public interface Log {
  2. boolean isDebugEnabled();
  3. boolean isTraceEnabled();
  4. void error(String s, Throwable e);
  5. void error(String s);
  6. void debug(String s);
  7. void trace(String s);
  8. void warn(String s);
  9. }

  

  统一定义了日志级别。
  适配器角色,以Jdk14LoggingImpl 为例,

  

  1. package org.apache.ibatis.logging.jdk14;
  2. import java.util.logging.Level;
  3. import java.util.logging.Logger;
  4. import org.apache.ibatis.logging.Log;
  5. /**
  6. * @author Clinton Begin
  7. */
  8. public class Jdk14LoggingImpl implements Log {
  9. private final Logger log;
  10. public Jdk14LoggingImpl(String clazz) {
  11. log = Logger.getLogger(clazz);
  12. }
  13. @Override
  14. public boolean isDebugEnabled() {
  15. return log.isLoggable(Level.FINE);
  16. }
  17. @Override
  18. public boolean isTraceEnabled() {
  19. return log.isLoggable(Level.FINER);
  20. }
  21. @Override
  22. public void error(String s, Throwable e) {
  23. log.log(Level.SEVERE, s, e);
  24. }
  25. @Override
  26. public void error(String s) {
  27. log.log(Level.SEVERE, s);
  28. }
  29. @Override
  30. public void debug(String s) {
  31. log.log(Level.FINE, s);
  32. }
  33. @Override
  34. public void trace(String s) {
  35. log.log(Level.FINER, s);
  36. }
  37. @Override
  38. public void warn(String s) {
  39. log.log(Level.WARNING, s);
  40. }
  41. }

  

  Jdk14LoggingImpl 通过继承关系实现Log,组合java.util.logging.Logger对象log; ,适配器实现继承的方法的时候通过关联对象log实现。
  4.mybatis怎么初始化日志适配器

 

  1. package org.apache.ibatis.logging;
  2. import java.lang.reflect.Constructor;
  3. /**
  4. * @author Clinton Begin
  5. * @author Eduardo Macarron
  6. */
  7. public final class LogFactory {
  8. /**
  9. * Marker to be used by logging implementations that support markers
  10. */
  11. public static final String MARKER = "MYBATIS";
  12. //记录当前使用的第三方日志组件所对应的适配器的构造方法
  13. private static Constructor<? extends Log> logConstructor;
  14. static {
  15. tryImplementation(new Runnable() {
  16. @Override
  17. public void run() {
  18. useSlf4jLogging();
  19. }
  20. });
  21. tryImplementation(new Runnable() {
  22. @Override
  23. public void run() {
  24. useCommonsLogging();
  25. }
  26. });
  27. tryImplementation(new Runnable() {
  28. @Override
  29. public void run() {
  30. useLog4J2Logging();
  31. }
  32. });
  33. tryImplementation(new Runnable() {
  34. @Override
  35. public void run() {
  36. useLog4JLogging();
  37. }
  38. });
  39. tryImplementation(new Runnable() {
  40. @Override
  41. public void run() {
  42. useJdkLogging();
  43. }
  44. });
  45. tryImplementation(new Runnable() {
  46. @Override
  47. public void run() {
  48. useNoLogging();
  49. }
  50. });
  51. }
  52. private LogFactory() {
  53. // disable construction
  54. }
  55. public static Log getLog(Class<?> aClass) {
  56. return getLog(aClass.getName());
  57. }
  58. public static Log getLog(String logger) {
  59. try {
  60. return logConstructor.newInstance(logger);
  61. } catch (Throwable t) {
  62. throw new LogException("Error creating logger for logger " + logger + ". Cause: " + t, t);
  63. }
  64. }
  65. public static synchronized void useCustomLogging(Class<? extends Log> clazz) {
  66. setImplementation(clazz);
  67. }
  68. public static synchronized void useSlf4jLogging() {
  69. setImplementation(org.apache.ibatis.logging.slf4j.Slf4jImpl.class);
  70. }
  71. public static synchronized void useCommonsLogging() {
  72. setImplementation(org.apache.ibatis.logging.commons.JakartaCommonsLoggingImpl.class);
  73. }
  74. public static synchronized void useLog4JLogging() {
  75. setImplementation(org.apache.ibatis.logging.log4j.Log4jImpl.class);
  76. }
  77. public static synchronized void useLog4J2Logging() {
  78. setImplementation(org.apache.ibatis.logging.log4j2.Log4j2Impl.class);
  79. }
  80. public static synchronized void useJdkLogging() {
  81. setImplementation(org.apache.ibatis.logging.jdk14.Jdk14LoggingImpl.class);
  82. }
  83. public static synchronized void useStdOutLogging() {
  84. setImplementation(org.apache.ibatis.logging.stdout.StdOutImpl.class);
  85. }
  86. public static synchronized void useNoLogging() {
  87. setImplementation(org.apache.ibatis.logging.nologging.NoLoggingImpl.class);
  88. }
  89. private static void tryImplementation(Runnable runnable) {
  90. if (logConstructor == null) {
  91. try {
  92. runnable.run();
  93. } catch (Throwable t) {
  94. // ignore
  95. }
  96. }
  97. }
  98. private static void setImplementation(Class<? extends Log> implClass) {
  99. try {
  100. Constructor<? extends Log> candidate = implClass.getConstructor(String.class);
  101. Log log = candidate.newInstance(LogFactory.class.getName());
  102. if (log.isDebugEnabled()) {
  103. log.debug("Logging initialized using '" + implClass + "' adapter.");
  104. }
  105. logConstructor = candidate;
  106. } catch (Throwable t) {
  107. throw new LogException("Error setting Log implementation. Cause: " + t, t);
  108. }
  109. }
  110. }

 

  上面适配器工厂我们通过mybatis的官方文档中的日志模块的使用来进行辅助理解:
  官网说明如下:
  Mybatis 通过使用内置的日志工厂提供日志功能。内置日志工厂将会把日志工作委托给下面的实现之一:
  SLF4J
  Apache Commons Logging
  Log4j 2
  Log4j
  JDK logging
  MyBatis 内置日志工厂会基于运行时检测信息选择日志委托实现。它会(按上面罗列的顺序)使用第一个查找到的实现。当没有找到这些实现时,将会禁用日志功能。

  这段话和源码中的static静态代码块相对应。

  1. static {
  2. tryImplementation(new Runnable() {
  3. @Override
  4. public void run() {
  5. useSlf4jLogging();
  6. }
  7. });
  8. tryImplementation(new Runnable() {
  9. @Override
  10. public void run() {
  11. useCommonsLogging();
  12. }
  13. });
  14. tryImplementation(new Runnable() {
  15. @Override
  16. public void run() {
  17. useLog4J2Logging();
  18. }
  19. });
  20. tryImplementation(new Runnable() {
  21. @Override
  22. public void run() {
  23. useLog4JLogging();
  24. }
  25. });
  26. tryImplementation(new Runnable() {
  27. @Override
  28. public void run() {
  29. useJdkLogging();
  30. }
  31. });
  32. tryImplementation(new Runnable() {
  33. @Override
  34. public void run() {
  35. useNoLogging();
  36. }
  37. });
  38. }

  

  官网中的说明顺序一样,
  以其中JdkLogging为例说明初始化日志适配过程:

  1. private static void tryImplementation(Runnable runnable) {
  2. if (logConstructor == null) {
  3. try {
  4. runnable.run();
  5. } catch (Throwable t) {
  6. // ignore
  7. }
  8. }
  9. }

  首先判断当前logConstructor日志适配器构造方法是否已经初始化,如果初始化则返回,如果没有初始化则执行run()方法,对应当前示例就是useJdkLogging()方法,

  

  1. public static synchronized void useJdkLogging() {
  2. 交给了setImplementation
  3. setImplementation(org.apache.ibatis.logging.jdk14.Jdk14LoggingImpl.class);
  4. }
  5. private static void setImplementation(Class<? extends Log> implClass) {
  6. try {
  7. //通过传入类型获取适配器构造方法 如果没有找到对应类型则返回null
  8. Constructor<? extends Log> candidate = implClass.getConstructor(String.class);
  9. Log log = candidate.newInstance(LogFactory.class.getName());
  10. if (log.isDebugEnabled()) {
  11. log.debug("Logging initialized using '" + implClass + "' adapter.");
  12. }
  13. //进行赋值
  14. logConstructor = candidate;
  15. } catch (Throwable t) {
  16. throw new LogException("Error setting Log implementation. Cause: " + t, t);
  17. }
  18. }

  

  按上面次序进行日志适配器的初始化。

  最后一个useNoLogging代表禁用日志功能,因为适配器NoLoggingImpl中什么都没有做。

  1. package org.apache.ibatis.logging.nologging;
  2. import org.apache.ibatis.logging.Log;
  3. /**
  4. * @author Clinton Begin
  5. */
  6. public class NoLoggingImpl implements Log {
  7. public NoLoggingImpl(String clazz) {
  8. // Do Nothing
  9. }
  10. @Override
  11. public boolean isDebugEnabled() {
  12. return false;
  13. }
  14. @Override
  15. public boolean isTraceEnabled() {
  16. return false;
  17. }
  18. @Override
  19. public void error(String s, Throwable e) {
  20. // Do Nothing
  21. }
  22. @Override
  23. public void error(String s) {
  24. // Do Nothing
  25. }
  26. @Override
  27. public void debug(String s) {
  28. // Do Nothing
  29. }
  30. @Override
  31. public void trace(String s) {
  32. // Do Nothing
  33. }
  34. @Override
  35. public void warn(String s) {
  36. // Do Nothing
  37. }
  38. }

 

    官网说明第二段
   你也可以调用以下任一方法来选择日志实现:

  1. org.apache.ibatis.logging.LogFactory.useSlf4jLogging();
  2. org.apache.ibatis.logging.LogFactory.useLog4JLogging();
  3. org.apache.ibatis.logging.LogFactory.useJdkLogging();
  4. org.apache.ibatis.logging.LogFactory.useCommonsLogging();
  5. org.apache.ibatis.logging.LogFactory.useStdOutLogging()

你应该在调用其它 MyBatis 方法之前调用以上的某个方法。另外,仅当运行时类路径中存在该日志实现时,日志实现的切换才会生效。
如果你的环境中并不存在 Log4J,你却试图调用了相应的方法,MyBatis 就会忽略这一切换请求,并将以默认的查找顺序决定使用的日志实现。

因为上面的方法为LogFactory中的静态方法,所以可以直接调用来初始化适配器工厂中的适配器构造方法(logConstructor),从而实现第三方日志组件的切换,并且该方法执行于static静态代码块之后。

 

本文参考:mybatis技术内幕,Mybatis官网

 

版权声明:本文为hopeofthevillage原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/hopeofthevillage/p/13063715.html