網站首頁 編程語言 正文
Mybatis技術內幕-設計模式與應用場景總結
1、適配器模式
主要解決由于接口不能兼容而導致類無法使用的問題,適配器模式會將需要適配的類轉換成調用者能夠使用的目標接口。主要涉及以下幾個角色:
目標接口Target:調用者能夠直接使用的接口;
需要適配的類Adaptee:Adaptee中有真正的業務邏輯,但是不能被調用者直接使用;
適配器:Adapter實現了Target接口,并包裝一個Adaptee對象,Adapter在實現Target接口中的方法時,會將調用委托給Adaptee對象的相關對象,由Adaptee中的方法完成具體的業務。
說明代碼:
/**
* 被適配者
*/
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
相關推薦
- 2023-01-02 Python利用socket實現多進程的端口掃描器_python
- 2022-08-19 python?GUI多行輸入文本Text的實現_python
- 2024-04-02 docker開機自啟設置
- 2022-04-30 DataGridView控件常用屬性介紹_C#教程
- 2022-10-01 Python類和對象基礎入門介紹_python
- 2022-07-29 Linux文件管理方法介紹_linux shell
- 2023-02-17 pytorch中nn.Flatten()函數詳解及示例_python
- 2022-08-05 Android自定義Span實現文字漸變效果_Android
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支