【面试精讲】MyBatis设计模式及源码分析,MyBatis设计模式实现原理
目录
5. 模板方法模式(Template Method Pattern)
工厂模式 - SqlSessionFactoryBuilder
MyBatis是一个流行的持久层框架,它极大地简化了数据库操作,提升了开发效率。在其底层实现中,MyBatis广泛采用了各种设计模式,这些设计模式不仅增加了框架的灵活性和可维护性,也为开发人员提供了丰富的使用场景和扩展能力。本文将深入探讨MyBatis所应用的主要设计模式及其在框架中的具体应用。
工厂模式是一种创建型模式,它提供了一种创建对象的最佳方式。在MyBatis中,SqlSessionFactoryBuilder
、SqlSessionFactory
等关键组件的创建过程就运用到了工厂模式。尤其是SqlSessionFactory
的创建,它是通过SqlSessionFactoryBuilder
读取MyBatis配置文件并构建出SqlSessionFactory
实例。这样做可以隔离复杂的初始化过程,使用户只需关注最终产出,而无需了解创建实例的复杂过程。
单例模式确保一个类只有一个实例,并提供一个全局访问点。在MyBatis中,SqlSessionFactory
的设计就是单例模式的经典应用。一旦通过SqlSessionFactoryBuilder
创建了SqlSessionFactory
实例后,该实例就会在应用程序中被复用,避免了重复创建实例带来的资源浪费。
建造者模式旨在将一个复杂对象的构建与其表示分离,使得同样的构建过程可以创建不同的表示。MyBatis中的XMLConfigBuilder
、XMLMapperBuilder
等类的设计采用了建造者模式。这些Builder类负责解析MyBatis的配置文件和映射文件,逐步构建出配置信息和映射信息。通过建造者模式,MyBatis将复杂的解析过程分解成一系列的步骤,使得代码更加清晰和易于维护。
代理模式为其他对象提供一个代理以控制对这个对象的访问。MyBatis中对Mapper接口的实现就是基于JDK动态代理机制。当调用Mapper接口的方法时,实际上是由MyBatis生成的代理类去执行。这种方式允许MyBatis在执行Mapper方法前后插入自定义逻辑,如开启事务、处理缓存等,从而大幅提升了灵活性和可扩展性。
模板方法模式定义了一个操作中的算法的骨架,而将一些步骤延迟到子类中。MyBatis的BaseExecutor
类就是一个模板方法模式的例证。它定义了数据库操作的基本流程,如查询、更新、提交事务等,而具体的执行逻辑则留给其子类(比如SimpleExecutor
、BatchExecutor
等)去实现。这样做的好处是复用了代码,同时又保留了灵活性。
策略模式定义了一系列的算法,并将每一个算法封装起来,使它们可以相互替换。MyBatis中的缓存策略、加载策略等正是策略模式的应用。例如,MyBatis允许用户配置不同的缓存实现(如EHCache、OSCache等),并在运行时根据配置动态选择。这种模式使得MyBatis具有很高的灵活性和扩展性。
观察者模式定义了一种依赖关系,当一个对象状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。在MyBatis中,Configuration
对象就是一个观察者,它会监听映射文件的解析事件,并在解析完成后更新自己的状态。通过观察者模式,MyBatis能够动态响应配置信息的变化,增强了框架的动态性和灵活性。
以上便是MyBatis中常见的几种设计模式及其应用。这些设计模式的运用大大提升了MyBatis的内聚性、可扩展性和灵活性,使得MyBatis成为了Java领域广泛使用的ORM框架之一。
在第一部分中,我们讨论了MyBatis使用的一些关键设计模式及其作用。现在,我们将深入探索这些设计模式在MyBatis源码中的具体实现原理。
SqlSessionFactory
实现原理:
SqlSessionFactory
的创建是通过SqlSessionFactoryBuilder
完成的。这个过程遵循了典型的工厂模式设计,SqlSessionFactoryBuilder
充当工厂的角色,负责生产SqlSessionFactory
对象。
public SqlSessionFactory build(Reader reader) {
// 使用XMLConfigBuilder解析配置文件
XMLConfigBuilder parser = new XMLConfigBuilder(reader, null, null);
return build(parser.parse());
}
在上述代码中,build
方法首先通过XMLConfigBuilder
解析给定的配置文件,然后根据解析结果构建出一个SqlSessionFactory
实例。这个过程封装了对象的创建逻辑,使得客户端代码无需直接与对象的创建细节打交道。
SqlSessionFactory
实现原理:
在MyBatis中,通常我们会将SqlSessionFactory
作为单例存在,以保证全局只有一个数据库连接池,从而节省资源。、
public class MyBatisUtil {
private static SqlSessionFactory sqlSessionFactory;
static {
try (InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml")) {
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
throw new ExceptionInInitializerError(e);
}
}
public static SqlSessionFactory getSqlSessionFactory() {
return sqlSessionFactory;
}
}
在这段代码中,利用静态初始化块加载配置并创建SqlSessionFactory
,确保了其单例性。此外,通过提供一个静态方法getSqlSessionFactory
来全局访问该实例,进一步体现了单例模式的特点。
实现原理:
MyBatis中对Mapper接口的实现是基于JDK动态代理的。当调用Mapper接口的方法时,实际上是委托给了由MyBatis动态生成的代理类。
public class MapperProxy<T> implements InvocationHandler {
private final SqlSession sqlSession;
private final Class<T> mapperInterface;
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 方法调用的处理逻辑
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
}
// 执行SQL操作
return sqlSession.selectList(mapperInterface.getCanonicalName() + "." + method.getName());
}
}
MyBatis在处理配置文件时,使用了建造者模式。XMLConfigBuilder
和XMLMapperBuilder
是两个具体的例子,它们负责解析MyBatis的配置文件和映射文件。
实现原理:这段代码展示了BaseExecutor
中的query
方法,这是一个模板方法,它定义了执行查询的基本流程,并将具体的查询逻辑委托给doQuery
方法。子类需要根据不同的需求实现doQuery
方法,比如SimpleExecutor
、ReuseExecutor
等都有各自的实现,这正是模板方法模式的典型应用。
public class XMLConfigBuilder extends BaseBuilder {
private boolean parsed;
private final XPathParser parser;
public XMLConfigBuilder(InputStream inputStream) {
this(new XPathParser(inputStream, true, null));
}
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
// 省略解析过程...
return configuration;
}
}
MyBatis的缓存策略使用了策略模式。MyBatis允许用户选择或自定义缓存实现,这是通过将缓存行为抽象成接口,并允许动态设置具体实现来实现的。
实现原理:Cache
接口定义了缓存的行为,而PerpetualCache
提供了Cache
接口的一个基础实现。MyBatis还支持更多缓存实现,如OSCache、Ehcache等,开发人员可以根据需要选择或自定义缓存策略,这体现了策略模式的灵活性。
public interface Cache {
void putObject(Object key, Object value);
Object getObject(Object key);
// 更多方法...
}
public class PerpetualCache implements Cache {
private Map<Object, Object> cache = new HashMap<>();
@Override
public void putObject(Object key, Object value) {
cache.put(key, value);
}
@Override
public Object getObject(Object key) {
return cache.get(key);
}
// 实现其他方法...
}
MyBatis利用观察者模式来实现插件功能。插件可以在MyBatis操作数据库的关键节点被插入,比如执行查询之前。这种机制使得用户能够在不修改MyBatis核心代码的情况下,扩展其功能。
实现原理:Interceptor
接口定义了插件需要实现的intercept
方法。SomePlugin
是一个具体的插件实现,它可以在方法调用前后执行额外的逻辑。这种方式使得MyBatis能够在运行时灵活地扩展功能,体现了观察者模式的特点。
public interface Interceptor {
Object intercept(Invocation invocation) throws Throwable;
// 更多方法...
}
public class SomePlugin implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
// 在执行前后添加额外的操作
Object returnObject = invocation.proceed();
// 在执行后处理
return returnObject;
}
}
深入探讨MyBatis的源码对于理解其设计模式的应用至关重要。由于篇幅限制,我们将通过几个关键组件的源码片段来揭示MyBatis是如何实现上文提到的设计模式的。请注意,下面的代码是简化版本,旨在帮助理解其核心原理。
SqlSessionFactoryBuilder
MyBatis通过SqlSessionFactoryBuilder
创建SqlSessionFactory
的过程是工厂模式的一个典型应用。
public class SqlSessionFactoryBuilder {
public SqlSessionFactory build(Reader reader) {
// 解析配置文件,构建Configuration对象
XMLConfigBuilder parser = new XMLConfigBuilder(reader);
Configuration config = parser.parse();
// 返回SqlSessionFactory实例
return new DefaultSqlSessionFactory(config);
}
}
SqlSessionFactory
虽然MyBatis不直接提供SqlSessionFactory
的单例实现,但在实际应用中,开发者通常会将其实现为单例模式,以下是一个简单的示例:
public class MyBatisUtil {
private static SqlSessionFactory sqlSessionFactory;
static {
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}
public static SqlSessionFactory getSqlSessionFactory() {
return sqlSessionFactory;
}
}
MapperProxyFactory
MyBatis使用JDK动态代理为Mapper接口生成代理对象,以便拦截接口方法的调用。
public class MapperProxy<T> implements InvocationHandler {
private final SqlSession sqlSession;
private final Class<T> mapperInterface;
public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface) {
this.sqlSession = sqlSession;
this.mapperInterface = mapperInterface;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 省略具体实现
return sqlSession.selectList(mapperInterface.getCanonicalName() + "." + method.getName());
}
}
public class MapperProxyFactory<T> {
private final Class<T> mapperInterface;
public MapperProxyFactory(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
public T newInstance(SqlSession sqlSession) {
MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface);
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[]{mapperInterface}, mapperProxy);
}
}
每个设计模式的实现都体现了MyBatis设计的巧妙和优雅,通过这些模式的应用,MyBatis成功地将框架的灵活性、可扩展性和维护性提升到了一个新的高度。希望通过这三部分的解析,你能对MyBatis的设计和实现有一个更加深入的理解。
如果本文对你有帮助 欢迎 关注 、点赞 、收藏 、评论, 博主才有动力持续创作!!!
📫作者简介:嗨,大家好,我是 小明 ,互联网大厂后端研发专家,2022博客之星TOP3 / 博客专家 / CSDN后端内容合伙人、InfoQ(极客时间)签约作者、阿里云签约博主、全网 6 万粉丝博主。
🍅 文末获取联系 🍅 👇🏻 精彩专栏推荐订阅收藏 👇🏻
专栏系列(点击解锁)
学习路线(点击解锁)
知识定位
全面讲解MySQL知识与企业级MySQL实战 🔥计算机底层原理🔥
更多【面试-【面试精讲】MyBatis设计模式及源码分析,MyBatis设计模式实现原理】相关视频教程:www.yxfzedu.com