Mybatis是如何通过mapper接口生成代理对象的

mybatis框架的使用非常广泛,使用者只需要开发很简单的代码就能实现很多业务需求,真的非常方便,简化开发的同时还规范了开发,也因此成为在持久映射框架中最为流行的框架之一。

使用mybatis开发的同学都应该接触过这样一种操作,我们只需要定义dao的接口,并没有具体的接口实现,却能够正常调用通过dao来执行sql,这是怎么做到的呢?

最近研究了一下mybatis的源码,通过理解源码得出是通过动态代理来实现的,这里做了一个简单的例子以方便我们理解,示例代码如下:

1.新建两个dao接口

package com.wisea.demoh2.mbatis.proxy;

public interface UserDao {

    @Insert
    int add(String str);

    @Update
    int update(String str);

}
package com.wisea.demoh2.mbatis.proxy;

public interface OrderDao {

    @Insert
    int add(String str);

    @Update
    int update(String str);

}

2.创建代理工厂类

package com.wisea.demoh2.mbatis.proxy;

import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class MapperProxyFactory<T> {

    private final Class<T> mapperInterface;
    private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<>();

    public MapperProxyFactory(Class<T> mapperInterface) {
        this.mapperInterface = mapperInterface;
    }

    public Class<T> getMapperInterface() {
        return mapperInterface;
    }

    public Map<Method, MapperMethod> getMethodCache() {
        return methodCache;
    }

    protected T newInstance(MapperProxy<T> mapperProxy){
        return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[]{mapperInterface}, mapperProxy);
    }

    public T newInstance(){
        final MapperProxy<T> mapperProxy = new MapperProxy<>(mapperInterface, methodCache);
        return newInstance(mapperProxy);
    }
}

3.创建代理类实现InvocationHandler接口

package com.wisea.demoh2.mbatis.proxy;

import java.io.Serializable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Map;

public class MapperProxy<T> implements InvocationHandler, Serializable {

    private final Class<T> mapperInterface;
    private final Map<Method, MapperMethod> methodCache;

    public MapperProxy(Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
        this.mapperInterface = mapperInterface;
        this.methodCache = methodCache;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (Object.class.equals(method.getDeclaringClass())) {
            try {
                return method.invoke(this, args);
            } catch (Throwable t) {

            }
        }
        final MapperMethod mapperMethod = cachedMapperMethod(method);

        return mapperMethod.execute(args);
    }

    private MapperMethod cachedMapperMethod(Method method) {
        MapperMethod mapperMethod = methodCache.get(method);
        if (mapperMethod == null) {
            mapperMethod = new MapperMethod(method, this);
            methodCache.put(method, mapperMethod);
        }
        return mapperMethod;
    }
}

4.创建代理处理的方法类

package com.wisea.demoh2.mbatis.proxy;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;

public class MapperMethod {

    private final Method method;
    private final MapperProxy mapperProxy;

    public MapperMethod(Method method, MapperProxy mapperProxy) {
        this.method = method;
        this.mapperProxy = mapperProxy;
    }

    public Object execute(Object[] args){
        System.out.println("MapperProxy: " + mapperProxy);
        System.out.println("execute method {" + method.getName() + "}" );


        // 读取mapper.xml中的语句块,并将其作为SqlStatement执行,
        // 方法名获取sql语句,参数通过代理的Invocation等获得
        // 有注解则处理注解中的参数
        
        // 此处只是简单的模拟一下
        final Annotation[] annotations = method.getAnnotations();
        for (Annotation annotation : annotations) {
            Class<? extends Annotation> aClass = annotation.annotationType();
            if (aClass.equals(Insert.class)) {
                System.out.println("execute insert {" + args[0] + "} completed");
            }
            if (aClass.equals(Update.class)) {
                System.out.println("execute update {" + args[0] + "} completed");
            }
        }
        return 1;
    }
}

5.编写测试类

package com.wisea.demoh2.mbatis.proxy;

public class ProxyMain {

    public static void main(String[] args) {
        System.out.println("实例化UserDao的代理工厂");
        MapperProxyFactory<UserDao> userDaoProxyFactory = new MapperProxyFactory<>(UserDao.class);

        UserDao target = userDaoProxyFactory.newInstance();
        System.out.println(target);

        target.add("chinoukin");

        target.update("chenyingqin");


        System.out.println("\n实例化OrderDao的代理工厂");
        MapperProxyFactory<OrderDao> OrderDaoProxyFactory = new MapperProxyFactory<>(OrderDao.class);

        OrderDao target1 = OrderDaoProxyFactory.newInstance();
        System.out.println(target1);

        target1.add("订单1");

        target1.update("订单xxxx");


    }
}

执行结果如下:

实例化UserDao的代理工厂
com.wisea.demoh2.mbatis.proxy.MapperProxy@b4c966a
MapperProxy: com.wisea.demoh2.mbatis.proxy.MapperProxy@b4c966a
execute method {add}
execute insert {chinoukin} completed
MapperProxy: com.wisea.demoh2.mbatis.proxy.MapperProxy@b4c966a
execute method {update}
execute update {chenyingqin} completed

实例化OrderDao的代理工厂
com.wisea.demoh2.mbatis.proxy.MapperProxy@7530d0a
MapperProxy: com.wisea.demoh2.mbatis.proxy.MapperProxy@7530d0a
execute method {add}
execute insert {订单1} completed
MapperProxy: com.wisea.demoh2.mbatis.proxy.MapperProxy@7530d0a
execute method {update}
execute update {订单xxxx} completed

Process finished with exit code 0

总结:

1.先为每一个DAO接口创建一个代理工厂

2.再通过代理工厂去创建代理对象,如上面的"MapperProxy: com.wisea.demoh2.mbatis.proxy.MapperProxy@b4c966a"

3.通过代理对象去调用实际的业务逻辑

  • 6
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值