Mybatis源码解析(二) -- 动态代理与池化技术

Mybatis动态代理与池化技术

概述

在mybatis中通过动态代理的方式,实例化Mapper接口,请求Mapper接口方法时,可直接与该方法对应的xml标签中的sql语句并执行,接下来我们来梳理一下请求的流程

动态代理技术原理

// 定义一个service接口
public interface IUserService {
    String getUserNameById(Integer id);
}



// 编译代理类
public class ServiceProxy implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if(proxy instanceof  IUserService) {
            System.out.println("IUserService的实例");
            System.out.println("执行方法名称为:" + method.getName());
        }
        return null;
    }
}

// 通过动态代理创建接口实例
public class Application {
    public static void main(String[] args) {
        // 生成代理类实例  参数解析    1.需要生成实例的接口类加载器   2.类类型实例    3.实现了InvocationHandler接口的实现类
        IUserService userService = (IUserService) Proxy.newProxyInstance(IUserService.class.getClassLoader(),new Class[]{IUserService.class},new ServiceProxy());
        userService.getUserNameById(1);
    }
  
}

执行结果如下
在这里插入图片描述

通过以上的代码实践和执行结果得出结论:动态代理技术虽然不编写接口实现类,但是需要编写代理类。被代理的接口方法执行是通过 invoke(Object target,Method method,Object[] args)统一调用

Mybatis生成Mapper接口实现类

将接口与XML绑定
  1. 定义接口及其接口方法
  2. 定义与之对应的xml文件
  3. 配置xml文件的nameSpace属性为接口类全路径
  4. 定义Select|Update|Delete|insert 标签,并且将该标签的id值设置为方法的名称

在这里插入图片描述

接口执行

在Mybatis中,IUserDao接口的代理类为一个叫MapperProxy类,当该接口调用方法时,通过invoke方法中的Method实例,可以知道接口调用的是那个方法,然后我们通过方法名称,可以获取到xml中与之对应的sql语句

public class MapperProxy <T> implements InvocationHandler, Serializable {
    // sql的实际执行者
    private SqlSession sqlSession;
    // Mapper接口类型
    private Class<T> mapperInterface;
    // Mapper接口方法映射
    private final Map<Method, MapperMethod> methodCache;

    public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface,Map<Method,MapperMethod> methodCache) {
        this.sqlSession = sqlSession;
        this.mapperInterface = mapperInterface;
        this.methodCache = methodCache;
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // Object类的方法
        if (Object.class.equals(method.getDeclaringClass())) {
            return method.invoke(this, args);
        } else {
            // 先获取mapperMethod
            final MapperMethod mapperMethod = cachedMapperMethod(method);
            return mapperMethod.execute(sqlSession, args);
        }
    }
    
    //缓存和获取MapperMethod
    private MapperMethod cachedMapperMethod(Method method) {
        MapperMethod mapperMethod = methodCache.get(method);
        if(mapperMethod == null) {
             mapperMethod = new MapperMethod(sqlSession.getConfiguration(), mapperInterface, method);
            methodCache.put(method,mapperMethod);
        }
        return mapperMethod;
    }
}

通过上面的源码,我们可以看到,调用接口方法后,会通过method实例去获取MapperMethod实例,并且最终执行调用

小结

Mybatis通过动态代理技术创建接口实例,并且在代理类存储与方法对应的sql语句。动态代理技术在这里的使用场景为,为一组具有相同操作的接口抽象调用过程。数据库的表不论是那种模型,仅有查询,删除,更新等操作,并且这些操作语法相同,在mybatis中通过编写xmlsql语句,再通过动态代理进行隐藏调用过程进行解耦。

Mybatis的数据源池化技术

无论是使用jpa技术,hibernate,mybatis,都会通过配置数据库连接池的方式来管理数据库连接,可对连接复用,资源控制。在传统的JDBC操作中是通过java.sql.Connection实例来对数据库进行操作,而JDBC的Java标准接口中,是通过实现javax.sqlDataSource接口来获取Connection实例。

Mybatis的数据源配置
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            // 配置非池化的数据源
            <dataSource type="UNPOOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis?useSSL=false"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <mapper resource="mapper/User_Mapper.xml"/>
    </mappers>

在上面的配置中我们可以看到数据源的类型为UNPOOLED,该配置是一个数据源类型的简要名称,我们可以在别名配置器中找到

    public Configuration() {
        // 增加jdbc 事务工厂
        typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
        // 数据源工厂
        typeAliasRegistry.registerAlias("DRUID", DruidDataSourceFactory.class);
        // 无池化的数据源工厂
        typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);
        // 池化的数据源工厂
        typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
    }

数据源的类型与对应的类型被封装在别名注册器中,当Mybatis加载配置时,可通过该名称找到对应类型,最后通过反射的形式创建数据源的实例类型

池化数据源
public class PooledDataSource implements DataSource {
    /**
     * 无池化的数据源
     * 基于包装模式,增强UnpooledDataSource的功能
     */
    private final UnpooledDataSource dataSource;
    private final PoolState state = new PoolState(this);
    /**
     * 最大的活跃连接数
     */
    protected int poolMaximumActiveConnections = 10;

    /**
     *  最大的空闲连接数
     */
    protected int poolMaximumIdleConnections = 5;

    /**
     *  在强制返回之前,池中连接被检查的时间
     *  ns数  默认为2s
     */
    protected int poolMaximumCheckoutTime = 20000;
    /**
     *  连接等待时间
     */
    protected int poolTimeToWait = 20000;
    /**
     * 发送到数据的侦测查询
     */
    protected String poolPingQuery = "NO PING QUERY SET";
    /**
     * 是否开启数据侦测查询   默认关闭
     */
    protected boolean poolPingEnabled = false;
    /**
     * 用来配置 poolPingQuery 多久使用一次
     */
    protected int poolPingConnectionsNotUsedFor = 0;
    private int expectedConnectionTypeCode;

以上是Mybatis池化数据源的连接池配置信息,通过这些配置可以控制连接池的资源使用。
但是在上面的属性中,最重要的属性是UnpooledDataSource类属性和PoolState类属性。

装饰器模式的应用

UnpooledDataSource是一个非池化的数据源,它的作用就是直接获取Connection,在这里是使用装饰器模式,UnpooledDataSource是被装饰的类,而PooledDataSource是装饰器类,它为UnpooledDataSource提供连接池化的额外能力。

享元模式的应用

PoolState类是一个管理活跃Connection和空闲Connection的实例
在这里插入图片描述
在该类中,我们可以看到两个分别存储空闲连接和活跃连接的列表,这就是数据源池化的最底层实现,并且下面还有很多属性,用于存储连接池的运行指标

动态代理Connection实现连接复用

通过上面的介绍,我们知道了连接池的实现是通过PoolState类实现的,但是为什么保存连接的列表类型是PooledConnection呢,不是应该是Connection吗?带着这个疑问我们继续深究源码。
在这里插入图片描述
发现,这个类竟然是一个实现了InvocationHandler接口的代理类,并且其中竟然还有两个Connection实例,分别是真实的实例和代理实例。接下来我们来梳理一下该类的运行逻辑

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

通过使用代理模式,监听是否调用了connection.close方法,调用了该方法就会将该方法放回连接池,然后通知其他线程来获取连接,以达到连接的复用。在这里自己代理的方式,可以理解为自己包装自己,自己给自己增加额外的职责,可以从装饰器模式的原理来理解

小结

在mybatis数据源中,基于装饰器模式,享元模式,代理模式实现,不得不说,设计模式的强大

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值