面试题系列(二):mybatis的Mapper接口实现过程和字符串组装实现方式

这篇文章说的到内容不是很难,但是在实际开发场景中经常用的,并且很容易会被问到的。下面一起看看今天要说的两道面试题。

Mapper只是一个接口并没有具体的方法实现,它是如何实现数据操作的?

在mybatis中对Mapper虽然是接口类,没有具体的实现,但是在mybatis的执行过程中,通过了动态代理的方式生成了对应的代理对象,在mybatis的binding包下面有主要的几个类分别是MapperMethodMapperProxyMapperProxyFactoryMapperRegistry。在MapperRegistry中的getMapper方法中会构建MapperProxyFactry对象,然后调用MapperProxyFactory中的newInstance方法,此方法会根据接口类生成对应的代理对象MapperProxy,此时使用的是JDK代理方式。在业务代码中调用接口类中方法的时候,实际是调用MapperProxy内的invoke方法,在invoke方法的参数中有一个method,可以通过method构建出对应的MapperStatement对象以及构建后执行的CRUD过程。

对应的MapperRegistry中的源码如下:

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    //根据type到knowMappers中获取MapperProxyFactory实例
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    if (mapperProxyFactory == null) {
        throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    }
    try {
        //mapperProxyFactory生成对象实例
        return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
        throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
}

先是构建MapperProxyFactory工厂类对象,然后调用newInstance方法。

在工厂类中实现的源码:

    
protected T newInstance(MapperProxy<T> mapperProxy) {
        //创建代理对象
        return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[]{mapperInterface}, mapperProxy);
    }

    public T newInstance(SqlSession sqlSession) {
        //构建MapperProxy对象
        final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
        //返回创建完成的代理对象
        return newInstance(mapperProxy);
    }

在这里就是构建代理对象的过程,并将构建完成的代理对象返回。MapperProxy实现了InvocationHandler类,使用的JDK的代理方式。

String、StringBuffer、StringBuilder的区别?

String是字符串类型,创建后是不可修改的,在做字符串组装的时候,每次都是开辟一个新的空间存储,会产生很多失去引用的字符串值,这些值会在常量池中占用大量的空间,另外在开辟新的内存以及对象引用变化的过程也是比较耗费性能的。

StringBuffer是在jdk1.0就出现的,在创建StringBuffer对象的时候,对象内部会维护两个字段信息,分别存储字符串值value和字符串的长度count,字符串值采用的是char类型数组,初始大小是16。在组装字符串的时候,首先会去判断count和新加入字符串的长度之和needSpace是否已经大于value数组的大小,如果没有大于直接将新字符串放入到char中的空闲空间中,如果大于了char数组的长度,就要对value数组进行扩容,在扩容的时候首先是将char扩大两倍,如果这个两倍长度还是没有needSpace大,就直接让value的长度设置为needSpace,如果大于needSpace就直接采用。这里在扩容的时候采用的是两倍的方式,在组装次数多的时候,扩容的次数就会变少,因此涉及到的新内存开辟和引用的变化就会少,大大的提高了性能。另外StringBuffer是线程安全的,对每个方法都加了synchronized关键字,这样同时增加了锁的竞争,从而导致另一种新能的消耗。

StringBuilder是在jdk1.5出现的,这个类的内部实现逻辑和StringBuffer是相同的,但是不同之处在于StringBuilder是完全放开的,不考虑线程安全问题,没有进行加锁,从而解决了锁竞争对性能消耗。

综合来说,如果是少量的字符串组装就直接使用String即可,如果字符串组装量比较大,且涉及到多线程操作同一个字符串的组装,那么就要使用StringBuffer来做,能保证线程安全,如果是单线程的大量字符串组装,不涉及到线程安全问题,那么就选择StringBuilder,性能更高。

关于字符串的组装,上面的是文字描述部分,要是想具体的了解内部源码和实现过程,可以参考:组装字符串String、StringBuilder、StringBuffer比较

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序猿洞晓

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值