日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

Mybatis技術內幕-設計模式與應用場景總結

作者:碼籠包 更新時間: 2022-01-28 編程語言

Mybatis技術內幕-設計模式與應用場景總結

1、適配器模式

主要解決由于接口不能兼容而導致類無法使用的問題,適配器模式會將需要適配的類轉換成調用者能夠使用的目標接口。主要涉及以下幾個角色:
目標接口Target:調用者能夠直接使用的接口;
需要適配的類Adaptee:Adaptee中有真正的業務邏輯,但是不能被調用者直接使用;
適配器:Adapter實現了Target接口,并包裝一個Adaptee對象,Adapter在實現Target接口中的方法時,會將調用委托給Adaptee對象的相關對象,由Adaptee中的方法完成具體的業務。
適配器模式UML
說明代碼:

/**
 * 被適配者
 */
public class Adaptee {
  public void adapteeMethod(String str) {
    System.out.println(str);
  }
}

/**
 * 調用者能夠直接使用的接口
 */
public interface Target {
  void printListContent(List<String> content);
}

/**
 * 由于Adaptee僅僅處理String,現在擴展為處理List<String>,調用方可以直接調用
 */
public class Adapter implements Target {
  Adaptee adaptee;
  @Override
  public void printListContent(List<String> content) {
    content.stream().forEach(str -> adaptee.adapteeMethod(str));
  }
}

對應類圖
在這里插入圖片描述

ATT:1、增加適配器之后,原有的調用是不會改變的,即項目之前對Adaptee的調用,是不需要修改的,這符合開放-閉合原則;

Mybatis場景
Mybatis內部調用其日志模塊時,使用了其內部接口(org.apache.ibatis.logging.Log),但是Log4j、Log4j2等第三方日志組件對外提供的接口各不相同,Mybatis為了集成和復用這些第三方日志組件(這里可以理解成,集成第三方組件,提供該功能,但是其實是為了對Log進行復用),在其日志模塊中提供了多種Adapter,將這些第三方日志組件對外的接口適配成了org.apache.ibatis.logging.Log接口,這樣Mybatis內部就可以統一通過org.apache.ibatis.logging.Log接口調用第三方日志組件的功能了。
在這里插入圖片描述

mybatis適配器源碼,這里的Log既是Target類也是Adaptee類,Log是外部調用的接口,Jdk14LoggingImpl等復用了Log

package org.apache.ibatis.logging;

/**
 * @author Clinton Begin
 */
public interface Log {

  boolean isDebugEnabled();

  boolean isTraceEnabled();

  void error(String s, Throwable e);

  void error(String s);

  void debug(String s);

  void trace(String s);

  void warn(String s);

}


package org.apache.ibatis.logging.jdk14;

import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.ibatis.logging.Log;

/**
 * @author Clinton Begin
 */
public class Jdk14LoggingImpl implements Log {

  private final Logger log;

  public Jdk14LoggingImpl(String clazz) {
    log = Logger.getLogger(clazz);
  }

  @Override
  public boolean isDebugEnabled() {
    return log.isLoggable(Level.FINE);
  }

  @Override
  public boolean isTraceEnabled() {
    return log.isLoggable(Level.FINER);
  }

  @Override
  public void error(String s, Throwable e) {
    log.log(Level.SEVERE, s, e);
  }

  @Override
  public void error(String s) {
    log.log(Level.SEVERE, s);
  }

  @Override
  public void debug(String s) {
    log.log(Level.FINE, s);
  }

  @Override
  public void trace(String s) {
    log.log(Level.FINER, s);
  }

  @Override
  public void warn(String s) {
    log.log(Level.WARNING, s);
  }

}

2、代理模式和JDK動態代理

靜態代理模式

靜態代理模式中涉及到的角色:
Suject:是程序中的業務邏輯接口;
RealSubject:是實現了Subject接口的真正業務類;
Proxy:是實現了Subject接口的代理類,其中封裝了RealSubject對象。在程序中不會直接調動RealSubject對象的方法,而是使用Proxy對象實現相關功能。
代理模式:Proxy.operation()方法的實現會調用RealSubject對象的operation()方法執行真正的邏輯,但是處理完業務邏輯。Proxy.operation()會在RealSubject.operation()方法調用前后進行預處理和相關的后置處理。
作用:可以控制程序對RealSubject對象的訪問,或是在執行業務處理的前后進行預處理和后置處理。
應用:延遲加載;
缺點:在編譯階段就要為每個RealSuject類創建一個對應的Proxy類,當需要代理的類很多時,這就出現大量的Proxy類。
演示代碼:

public interface Subject {
  void operation();
}

public class RealSubject implements Subject {
    @Override
    public void operation() {
        System.out.println("realSubject operation");
    }
}


public class Proxy implements Subject {
    RealSubject realSubject;

    @Override
    public void operation() {
        System.out.println("before.....");
        realSubject.operation();
        System.out.println("after.....");
    }
}

動態代理模式

解決靜態中代理類過多的問題,通過動態創建代理類并且通過指定類加載器加載,然后在創建代理對象時將InvokerHandler.invoke()方法,并最終調用真正業務對象的相應方法。

代碼演示:

/**
 * 對于需要相同代理行為的業務類,只需要提供一個InvocationHandler實現即可。
 * 在程序運行時,JDK會為每一個RealSubject類動態生成代理類并加載到JVM中,之后創建對應的代理對象
 */
public class TestInvokerHandler implements InvocationHandler {
    //真正的業務對象,也就是RealSubject對象
    private Object target;

    public TestInvokerHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("在執行業務方法之前的前置處理....");
        //真正的業務執行過程
        Object result = method.invoke(target, args);
        System.out.println("在執行業務方法之后的后置處理....");
        return result;
    }

    public Object getProxy() {
        /**
         * 創建代理對象
         * 1、加載動態生成的代理類的類加載器
         * 2、業務類實現的接口
         * 3、InvokerHandler對象(JDK動態代理的核心)
         */
        return java.lang.reflect.Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
                target.getClass().getInterfaces(), this);
    }

    public static void main(String[] args) {
        Subject subject = new RealSubject();
        TestInvokerHandler testInvokerHandler = new TestInvokerHandler(subject);
        //獲取代理對象
        Subject proxy = (Subject) testInvokerHandler.getProxy();
        //調用代理對象的方法,它會調用TestInvokeHandler.invoke()方法
        //invoke中會調用目標類的真正業務執行方法
        proxy.operation();
    }
}

Mybatis場景

JDBC調試:在Mybatis的日志模塊中有一個JDBC包,它并不是將日志信息通過JDBC保存到數據庫中,而是通過JDK動態代理的方式,將JDBC操作通過指定的日志框架打印出來,這個功能通常在開發階段使用,它可以輸出SQL語句,用戶傳入的綁定參數,SQL語句影響行數等等信息,對調試程序來說是很重要的。

在這里插入圖片描述
ConnectionLogger繼承了BaseJdbcLogger抽象類,其中封裝了Connection對象并且同時實現了InvocationHandler接口。ConnectionLogger.newInstance()方法會為其封裝的Connection對象創建相應的代理對象。
invoke()方法是代理對象的核心方法,它為preparedStatement(),preparedCall(),createStatement()等等方法了代理。
源碼:


package org.apache.ibatis.logging.jdbc;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.Statement;

import org.apache.ibatis.logging.Log;
import org.apache.ibatis.reflection.ExceptionUtil;

public final class ConnectionLogger extends BaseJdbcLogger implements InvocationHandler {

    private final Connection connection;

    private ConnectionLogger(Connection conn, Log statementLog, int queryStack) {
        super(statementLog, queryStack);
        this.connection = conn;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] params) throws Throwable {
        try {
            //如果調用的是從Object繼承的方法,則直接調用,不做任何處理
            if (Object.class.equals(method.getDeclaringClass())) {
                return method.invoke(this, params);
            }
            //調用指定方法的時候,創建相應的Statement對象,并為該Statement創建代理對象并返回該代理對象
            if ("prepareStatement".equals(method.getName()) || "prepareCall".equals(method.getName())) {
                if (isDebugEnabled()) {
                    debug(" Preparing: " + removeExtraWhitespace((String) params[0]), true);
                }
                //調用Connection封裝的preparedStatement()方法,得到PreparedStatement對象
                PreparedStatement stmt = (PreparedStatement) method.invoke(connection, params);
                //為該PreparedStatement對象創建代理對象
                stmt = PreparedStatementLogger.newInstance(stmt, statementLog, queryStack);
                return stmt;
            } else if ("createStatement".equals(method.getName())) {
                Statement stmt = (Statement) method.invoke(connection, params);
                stmt = StatementLogger.newInstance(stmt, statementLog, queryStack);
                return stmt;
            } else {
               //其他方法則直接調用底層Connection對象的相應方法
                return method.invoke(connection, params);
            }
        } catch (Throwable t) {
            throw ExceptionUtil.unwrapThrowable(t);
        }
    }


    public static Connection newInstance(Connection conn, Log statementLog, int queryStack) {
        InvocationHandler handler = new ConnectionLogger(conn, statementLog, queryStack);
        ClassLoader cl = Connection.class.getClassLoader();
        return (Connection) Proxy.newProxyInstance(cl, new Class[]{Connection.class}, handler);
    }

    public Connection getConnection() {
        return connection;
    }

}

3、單例模式

在整個系統中,單例類只能有一個實例對象,并且向外提供獲取該實例對象的方法;
mybatis中應用場景:VFS虛擬文件系統;

4、工廠方法模式

5、裝飾器模式

Cache接口及其實現

6、建造者模式

BaseBuilder
XMLBaseBuilder

7、組合模式

8、模板方法模式

9、策略模式

10、責任鏈模式

原文鏈接:https://blog.csdn.net/Abracadabra__/article/details/112734683

欄目分類
最近更新