[Mybatis源码篇]三、MapperProxyFactory(映射器代理工厂)的实现原理

再次回顾Mybatis的基本用法

1、定义Mapper接口

2、在xml(或注解)中写sql

mybatis帮我们屏蔽了所有和数据库相关的操作,我们只需要给他提供参数、sql、标注返回值的类型即可。

通过mapper接口我们可以传递参数、获取返回值;通过xml或者注解我们可以提供需要执行的sql。那么问题来了,究竟是谁在干活?我们经常在service中注入的dao来自于哪里?

答案自然是————代理

代理

那些年我对代理的偏见

代理在我的印象中一直使用来增强对象的,通常这么用

XXXXXX在目标对象方法执行前,做一些事(如日志记录)XXXXXXXX

调用目标对象的方法
method.invoke(target, args)


XXXXXX在目标对象方法执行后,做一些事(如日志记录)XXXXXXXX

大概是这个流程。

但实际上,代理不仅仅可以增强被代理对象的功能,也可以“无中生有”————给它一个接口,它能还你一个实实在在的对象。

实战

在不看mybatis源码的基础上,实现一个功能。

定义一个Dao接口,接口中至少得有一个方法。

我们通过jdk代理获得代理对象,并在service中使用这个代理对象。

Service大概如下

public class HelloService {

    private HelloDao helloDao;

    public HelloService(HelloDao helloDao) {
        this.helloDao = helloDao;
    }

    public void test() {
        helloDao.sayHello("小明同学");
    }

    public static void main(String[] args) {
        HelloService helloService = new HelloService(ProxyUtils.proxy(HelloDao.class, new MapperHandler(HelloDao.class)));
        helloService.test();
    }
}

补充上图用到的一些组件

接口

public interface HelloDao {

    @Select("select * from hello")
    void sayHello(String name);
}

自定义的注解

@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface Select {
    String value();
}

代理工具类

public class ProxyUtils {

    public static <T> T proxy(Class<T> clazz, InvocationHandler invocationHandler) {
        return (T)Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz}, invocationHandler);
    }
}

InvocationHandler实现

public class MapperHandler implements InvocationHandler {

    private Class<?> clazz;

    public MapperHandler(Class<?> helloDao) {
        this.clazz = helloDao;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if(args != null) {
            for (Object arg : args) {
                System.out.println("通过代理拿到了参数:" + arg);
            }
        }

        for (Method declaredMethod : clazz.getDeclaredMethods()) {
            for (Parameter parameter : declaredMethod.getParameters()) {
                System.out.println("通过反射拿到了参数以及类型等:" + parameter.getName() + " , " + parameter.getType());
            }
            for (Annotation annotation : declaredMethod.getAnnotations()) {
                if(annotation instanceof Select) {
                    Select select = (Select) annotation;
                    String value = select.value();
                    System.out.println("拿到了sql: " + value);
                }
            }
        }
        return "执行结果";
    }
}

运行:

 通过反射拿到了接口标注的所有信息,并且成功的生成了代理类。

 总结一下如何通过代理实现“无中生有”。或者说通过接口直接生成代理对象,与通过类生成代理对象有何区别?

第一点,“无中生有”在invoke方法中不需要调用被代理对象的方法(因为压根没有被代理对象)

 第二点,创建代理对象时,因为clazz是接口,所以newProxyInstance的第二个参数直接传递的是clazz本身,而非之前的  clazz.getInterfaces()

 MapperProxyFactory

回头看Mybatis的代理工厂,似乎就很清晰了

 似乎与我们的做法一致

 mapperProxy其实就是InvocationHandler

至于MapperMethod是什么,以及代理对象如何执行sql的,后面总结。

如有错误,欢迎批评指正!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值