转载:https://mp.weixin.qq.com/s/_6nyhaWX15mh3mkj8Lb0Zw

Mybatis中声明一个interface接口,没有编写任何实现类,Mybatis就能返回接口实例,并调用接口方法返回数据库数据,你知道为什么不?

动态代理的功能:通过拦截器方法回调,对目标target方法进行增强。

言外之意就是为了增强目标target方法。

上面这句话没错,但也不要认为它就是真理,殊不知,动态代理还有投鞭断流的霸权,连目标target都不要的科幻模式。

注:本文默认认为,读者对动态代理的原理是理解的,如果不明白target的含义,难以看懂本篇文章,建议先理解动态代理。

1. 自定义JDK动态代理之投鞭断流实现自动映射器Mapper

首先定义一个pojo。

public class User {
    private Integer id;
    private String name;
    private int age;     public User(Integer id, String name, int age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }
    // getter setter
}

再定义一个接口UserMapper.java。

public interface UserMapper {
    public User getUserById(Integer id);    
}

接下来我们看看如何使用动态代理之投鞭断流,实现实例化接口并调用接口方法返回数据的。

自定义一个InvocationHandler。

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy; public class MapperProxy implements InvocationHandler {     @SuppressWarnings("unchecked")
    public <T> T newInstance(Class<T> clz) {
        return (T) Proxy.newProxyInstance(clz.getClassLoader(), new Class[] { clz }, this);
    }     @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (Object.class.equals(method.getDeclaringClass())) {
            try {
                // 诸如hashCode()、toString()、equals()等方法,将target指向当前对象this
                return method.invoke(this, args);
            } catch (Throwable t) {
            }
        }
        // 投鞭断流
        return new User((Integer) args[0], "zhangsan", 18);
    }
}

上面代码中的target,在执行Object.java内的方法时,target被指向了this,target已经变成了傀儡、象征、占位符。在投鞭断流式的拦截时,已经没有了target。

写一个测试代码:

public static void main(String[] args) {
    MapperProxy proxy = new MapperProxy();     UserMapper mapper = proxy.newInstance(UserMapper.class);
    User user = mapper.getUserById(1001);     System.out.println("ID:" + user.getId());
    System.out.println("Name:" + user.getName());
    System.out.println("Age:" + user.getAge());     System.out.println(mapper.toString());
}

output:

ID:1001
Name:zhangsan
Age:18
x.y.MapperProxy@6bc7c054

这便是Mybatis自动映射器Mapper的底层实现原理。

可能有读者不禁要问:你怎么把代码写的像初学者写的一样?没有结构,且缺乏美感。

必须声明,作为一名经验老道的高手,能把程序写的像初学者写的一样,那必定是高手中的高手。这样可以让初学者感觉到亲切,舒服,符合自己的Style,让他们或她们,感觉到大牛写的代码也不过如此,自己甚至写的比这些大牛写的还要好,从此自信满满,热情高涨,认为与大牛之间的差距,仅剩下三分钟。

2. Mybatis自动映射器Mapper的源码分析

首先编写一个测试类:

public static void main(String[] args) {
        SqlSession sqlSession = MybatisSqlSessionFactory.openSession();
        try {
            StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
            List<Student> students = studentMapper.findAllStudents();
            for (Student student : students) {
                System.out.println(student);
            }
        } finally {
            sqlSession.close();
        }
    }

Mapper长这个样子:

public interface StudentMapper {
    List<Student> findAllStudents();
    Student findStudentById(Integer id);
    void insertStudent(Student student);
}

org.apache.ibatis.binding.MapperProxy.java部分源码。

public class MapperProxy<T> implements InvocationHandler, Serializable {

  private static final long serialVersionUID = -6424540398559729838L;
  private final SqlSession sqlSession;
  private final Class<T> mapperInterface;
  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 {
    if (Object.class.equals(method.getDeclaringClass())) {
      try {
        return method.invoke(this, args);
      } catch (Throwable t) {
        throw ExceptionUtil.unwrapThrowable(t);
      }
    }
    // 投鞭断流
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    return mapperMethod.execute(sqlSession, args);
  }
  // ...

org.apache.ibatis.binding.MapperProxyFactory.java部分源码。

public class MapperProxyFactory<T> {

  private final Class<T> mapperInterface;

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

这便是Mybatis使用动态代理之投鞭断流。

3. 接口Mapper内的方法能重载(overLoad)吗?(重要)

类似下面:

public User getUserById(Integer id);
public User getUserById(Integer id, String name);

Answer:不能。

原因:在投鞭断流时,Mybatis使用package+Mapper+method全限名作为key,去xml内寻找唯一sql来执行的。

类似:key=x.y.UserMapper.getUserById,那么,重载方法时将导致矛盾。对于Mapper接口,Mybatis禁止方法重载(overLoad)。

MyBatis底层实现原理: 动态代理的运用的更多相关文章

  1. Hibernate学习--hibernate延迟加载原理-动态代理(阿里电面)

    在正式说hibernate延迟加载时,先说说一个比较奇怪的现象吧:hibernate中,在many-to-one时,如果我们设置了延迟加载,会发现我们在eclipse的调试框中查看one对应对象时,它 ...

  2. mybatis 基础1(动态代理)

    我目前使用的是mybatis 3.3.0版本. 可使用 1.xml文本, 2.dao类, 3.sqlSession.getMapper(Class<T> type), 生成sql类, 原理 ...

  3. Spring AOP实现原理-动态代理

    目录 代理模式 静态代理 动态代理 代理模式 我们知道,Spring AOP的主要作用就是不通过修改源代码的方式.将非核心功能代码织入来实现对方法的增强.那么Spring AOP的底层如何实现对方法的 ...

  4. 浅析Spring中AOP的实现原理——动态代理

    一.前言   最近在复习Spring的相关内容,刚刚大致研究了一下Spring中,AOP的实现原理.这篇博客就来简单地聊一聊Spring的AOP是如何实现的,并通过一个简单的测试用例来验证一下.废话不 ...

  5. AOP的实现原理——动态代理

    IOC负责将对象动态的 注入到容器,从而达到一种需要谁就注入谁,什么时候需要就什么时候注入的效果,可谓是招之则来,挥之则去.想想都觉得爽,如果现实生活中也有这本事那就爽 歪歪了,至于有多爽,各位自己脑 ...

  6. 新秀学习SSH(十四)——Spring集装箱AOP其原理——动态代理

    之前写了一篇文章IOC该博客--<Spring容器IOC解析及简单实现>,今天再来聊聊AOP.大家都知道Spring的两大特性是IOC和AOP. IOC负责将对象动态的注入到容器,从而达到 ...

  7. MyBatis 源码分析——动态代理

    MyBatis框架是如何去执行SQL语句?相信不只是你们,笔者也想要知道是如何进行的.相信有上一章的引导大家都知道SqlSession接口的作用.当然默认情况下还是使用DefaultSqlSessio ...

  8. Mybatis进阶学习笔记——动态代理方式开发Dao接口、Dao层(推荐第二种)

    1.原始方法开发Dao Dao接口 package cn.sm1234.dao; import java.util.List; import cn.sm1234.domain.Customer; pu ...

  9. Spring事务管理机制的实现原理-动态代理

    之前在做项目中遇到spring无法进行事务代理问题,最后发现是因为没有写接口,原因当时明白了,看到这篇文章写的清楚些,转过来 我们先来分析一下Spring事务管理机制的实现原理.由于Spring内置A ...

随机推荐

  1. 全能中间件 REST API 使用手册

    全能中间件 REST API 使用手册 Ver:17.6.24 技术支持QQ:64445322 QQ群:339616649    任何第三方应用或网站都可以通过使用开放API为用户提供实时优质的服务. ...

  2. PAT(B) 1051 复数乘法(Java)

    题目链接:1051 复数乘法 (15 point(s)) 参考博客:1051 复数乘法(PAT 乙级 C++实现)-guangjinzheng 题目描述 复数可以写成 (A+Bi) 的常规形式,其中 ...

  3. TZOJ3133: 求对称点

    #include<stdio.h> int main() { double a,b,c,d,e,f,g,h,i; while(~scanf("%lf %lf %lf %lf %l ...

  4. 利用strace & Perf分析MySQL

    strace介绍及用途 strace是一个用于诊断,分析linux用户态进程的工具 类似的工具pstrace,lsof,gdb,pstrack strace观察mysqld对my.cnf 配置文件的加 ...

  5. 解决batik使用JScrollPane显示svg图滚动条不显示的问题

    // 必须使用batik提供的JSVGScrollPane,使用swing自己的组件JScrollPane初始化时滚动条不会显示. JSVGScrollPane svgJScrollPane = ne ...

  6. elk 流程图

    ELK流程图 单纯使用ElK实现分布式日志收集缺点: 1.logstash太多了,扩展不好. 2.读取IO文件,可能会产生日志丢失 3.不是实时性 这时候就需要引入 kafka. kafka基于主题模 ...

  7. Linux下如何挂载文件,并设置开机自动挂载

    首先保证服务端安装了 查看是否安装命令: nfsstat yum install nfs-utils 安装nfs-utils 192.168.50.85(服务端)192.168.50.83(客户端) ...

  8. 遇到 GLFW 我的demo可以运行 但是公司的程序调用我的so运行不起来

    //to do 原       因:  发现 自身demo的程序的shaders更新了  但是公司程序却没有更新 解决办法:更新公司程序的shaders 为最新版本 吸取的教训: 不仅仅要更新公司程序 ...

  9. vue 使用vue-video-player播放hls格式视频

    安装 vue-video-player   在 “ devDependencies ” 中 安装  videojs-contrib-hls 在“ dependencies ”中   main.js 中 ...

  10. Java 之 Junit 单元测试

    Junit 单元测试 测试分类: 1.黑盒测试:不需要写代码,给输入值,看程序是否能够输出期望的值. 2.白盒测试:需要写代码.关注程序具体的执行流程. Junit 测试:(属于白盒测试) 步骤: 1 ...