一、源码分析概述
mybatis架构分析
包分析 详见脑图:java学习/享学架构师课程/03.mybaits/mybatis源码结构.xmind
谈谈设计模式的原则:
- 单一职责原则:一个类或者一个接口只负责唯一项职责,尽量设计出功能单一的接口。
- 依赖倒转原则:高层模块不应该依赖底层模块具体实现,接口高层与底层。既面向接口编程,当实现发生变化时,只需提供新的实现类,不需要修改高层模块代码。
- 开放-封闭原则:程序对外扩展开放,对修改关闭,换句话说,当需求发生变化时,我们可以通过添加新模块来满足新需求,而不是通过修改原来的是现代码来满足新需求。
高级开发:面向接口编程
二、日志模块分析
2.1、基础支撑层源码分析—日志模块,使用的设计模式:适配器模式、代理模式
-
- Mybatis没有提供日志的实现类,需要接入第三方日志组件,但第三方日志组件都有各自的log级别,且各不相同,mybatis统一提供了trace、debug、warn、error四个级别(适配器模式解决)
mybatis中,对应日志组件的实现类,如log4jImpl,就是log4j的适配器。在logging包中。
2.2、适配器模式
适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁,将一个类的接口转换成客户希望的另一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
Target:目标角色,期待得到的接口
Adaptee:适配者角色,被适配的接口
adapter:适配器角色,将源接口转换成目标接口
适用场景:当调用双方都不太容易修改的时候,为了复用现有组件可以使用适配器模式,在系统中接入第三方组件的时候经常被用到。
注意:如果系统中存在过多的适配器,会增加系统的复杂性,设计人员应考虑对系统进行重构。
mybatis中适配器的使用
代理模式那些事:
定义:给目标对象提供一个代理对象,并由代理对象控制对目标对象的引用
目的:
1、通过因考入代理对象的方式来间接访问目标对象,防止直接访问目标对象给系统带来不必要的复杂性;
2、通过代理对象对原有的业务增强;
为什么使用代理模式?
代理模式给我们带来的便利:
作为中介解耦客户端和真实对象,保护真实对象安全;
防止直接访问目标对象给系统带来不必要的复杂性;
对业务进行增强,增强点多样化;(AOP)
Connection代理对象(connectionLogger)能够返回一个prepareStatement的代理对象(prepareStatementLogger),让prepareStatement也具备日志能力打印参数;
prepareStatement的代理对象(prepareStatementLogger)能够返回一个resultSet的代理对象(ResultSetLogger),让resultSet也具备日志打印能力;
simpleExcutor类才是真正访问数据库的类
org.apache.ibatis.executor.SimpleExecutor的doQuery方法
org.apache.ibatis.executor.SimpleExecutor#prepareStatement方法生成Statement
我们可以看到,getConnection(statementLog)方法里是用connection的代理对象connectionLogger进行连接初始化的
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws sqlException {
Connection connection = this.getConnection(statementLog);
Statement stmt = handler.prepare(connection, this.transaction.getTimeout());
handler.parameterize(stmt);
return stmt;
}
protected Connection getConnection(Log statementLog) throws sqlException {
Connection connection = this.transaction.getConnection();
return statementLog.isDebugEnabled() ? ConnectionLogger.newInstance(connection, statementLog, this.queryStack) : connection;
}
三、数据源模块分析
3.1、基础支撑层源码分析 数据源模块需求。使用的设计模式:工厂模式
- 常见的数据源组件都实现了javax.sql.DataSource接口;
- mybatis不但要能集成第三方的数据源组件,自身也提供了数据源的实现;
- 一般情况下,数据源的初始化过程参数较多,比较复杂;
3.2、数据源模块类图
连接池的数据结构
PooledConnection:使用动态代理封装了真正的数据库连接对象;
PoolState:用于管理PooledConnection对象状态的组件,通过两个list分别管理空闲状态的连接资源和活跃状态的连接资源;
PooledDataSource:一个简单、同步的、线程安全的数据库连接池;
UnpooledDataSource:不使用数据源连接池;
pooledConnection对connection的增强,增加操作前的连接有效性检查、拦截等
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
if ("close".hashCode() == methodName.hashCode() && "close".equals(methodName)) {
this.dataSource.pushConnection(this);
return null;
} else {
try {
if (!Object.class.equals(method.getDeclaringClass())) {
this.checkConnection();
}
return method.invoke(this.realConnection, args);
} catch (Throwable var6) {
throw ExceptionUtil.unwrapThrowable(var6);
}
}
}
private void checkConnection() throws sqlException {
if (!this.valid) {
throw new sqlException("Error accessing PooledConnection. Connection is invalid.");
}
}
poolconnection参数意义
视频问题1:为什么在使用jdbc连接数据库的时候,使用Class.forName("com.MysqL.jdbc.Driver")时,就能够使用数据库的驱动了?
因为在jdbc的驱动类com.MysqL.jdbc.Driver有这么一段静态代码块,注释就是将自己注册到驱动管理器DriverManager中
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
//
// Register ourselves with the DriverManager
//
static {
try {
java.sql.DriverManager.registerDriver(new Driver());
} catch (sqlException E) {
throw new RuntimeException("Can't register driver!");
}
}
为什么调用registerDriver方法new Driver()就能将驱动注册到DriverManager中呢?
在UnpooledDataSource类中有这么段静态代码,这里会将所有的驱动扫描出来,放到缓存中
static {
Enumeration drivers = DriverManager.getDrivers();
while(drivers.hasMoreElements()) {
Driver driver = (Driver)drivers.nextElement();
registeredDrivers.put(driver.getClass().getName(), driver);
}
}
PoolState各个参数的意义
List<PooledConnection> idleConnections 空闲连接资源
List<PooledConnection> activeConnections 活跃连接资源
正在上传…重PooledDataSource获取和归还连接过程
获取连接 getConnection()
源码位置:org.apache.ibatis.datasource.pooled.PooledDataSource#popConnection
流程图
归还连接 pushConnection()
源码位置:org.apache.ibatis.datasource.pooled.PooledDataSource#pushConnection
3.3、工厂模式
- 简单工厂模式
优点:客户端免除了直接创建产品对象的责任,而仅仅负责调用,对象创建和对象使用的职责解耦
缺点:不符合设计原则之单一原则和开闭原则,对于需求的扩展需要修改代码
使用场景:对象比较单一,需求不复杂的场景
使用:需要一个工厂接口,一个工厂类实现工厂接口,目标类。工厂类通过简单判断创建目标类。
- 工厂模式
工厂模式属于创建型模式,它提供了一种创建对象的最佳方式。定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行
工厂接口(Factory):工厂接口是工厂方法模式的核心接口,调用者会直接和工厂接口交互用于偶去具体的产品实现类;
具体工厂类(ConcreteFactory):是工厂接口的实现类,用于实例化产品对象,不同的具体工厂会根据需求实例化不同的产品实现类;
产品接口(Product):产品接口用于定义产品类的功能,具体工厂类产生的所有产品都必须事先这个接口。调用者与产品接口直接交互,这是调用者最关心的接口;
具体产品类(ConcreteProduct):实现产品接口的实现类,具体产品中定义了具体的业务逻辑;
使用逻辑:
定义一个工厂接口
定义一个产品接口
具体工厂实现工厂接口,完成生产工作,生产出实现产品接口的具体产品类
- 抽象工厂模式
概念:
所谓抽象工厂模式就是提供一个接口,用于创建相关或者依赖对象的家族,而不需要明确指定具体类。它允许客户端使用抽象的接口来创建一组相关的产品,而不需要关心实际产出的具体产品是什么。这样一来,客户就可以从具体的产品中被解耦。它的优点是隔离了具体类的生成,使得客户端不需要知道什么被创建了,而缺点就在于新增新的行为会比较麻烦,因为当添加一个新的产品对象时,需要更改接口及其下的所有子类。
特点:
可以在类的内部对产品族中相关联的多等级产品共同管理,而不必专门引入多个新的类来进行管理。
其缺点是:当产品族中需要增加一个新的产品时,所有的工厂类都需要进行修改。
抽象工厂模式的主要角色 :
- 抽象工厂(Abstract Factory):提供了创建产品的接口,它包含多个创建产品的方法 new Product(),可以创建多个不同等级的产品。
- 具体工厂(Concrete Factory):主要是实现抽象工厂中的多个抽象方法,完成具体产品的创建。
- 抽象产品(Product):定义了产品的规范,描述了产品的主要功能和特性,抽象工厂模式有多个抽象产品。
- 具体产品(Concrete Product):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间是多对一的关系。
为什么要使用工厂模式?
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。